2015-12-14 18 views
15

Sono davvero un nuovo a Java concorrenza e sto cercando di attuare le seguenti specifiche:Come possono due thread essere "in" un metodo "sincronizzato"

  • Abbiamo un parcheggio che ha qualche park spots
  • Ogni auto è rappresentata come una filettatura che cambia continuamente lo stato della vettura da Driving - Parking. Ogni macchina ha il suo posto di parcheggio.
  • Quando l'auto è nello stato di parcheggio, tenta di parcheggiare in un punto (non è necessario il suo posto). Se il posto è libero allora parcheggia altrimenti salterà questa fase di parcheggio e tornerà a guidare.
  • L'auto rimane sul posto a meno che il proprietario dello spot non voglia parcheggiare.

questa non è la specifica esatta, ma l'unico problema che ho è il seguente:

io non sono in grado di rendere la vettura saltare il turno. Se due auto scelgono gli stessi posti, una viene parcheggiata e l'altra attende che il parco sia libero. Quale non è il bahvior che voglio. La mia prima idea era quella di sincronizzare semplicemente la lettura e scrittura ad una variabile occupata:

class Car implements Runnable { 
    private CarState state = CarState.driving 

    run { 
     while(true) { 
      switch(state) { 
      case driving: 
       System.out.println(this + " driving."); 
       state = parking; 
       break; 
      case parking: Spot s = CarPark.getRandomSpot(); 

       if(s.willingToPark(this)) { 
        System.out.println(s + " occupied. " + this 
        + " skip park turn."); 
       } else { 
        s.park(this); 
       } 
       state = driving; 
      } 
     } 

    } 
} 

class Spot { 
    private boolean occupied = false; 
    private Car owner = new Car(...); 

    synchronized boolean willingToPark(Car c) { 
     if(occupied) { 
      return true; 
     } else { 
      occupied = true; 
      return false; 
    } 

    synchronized void park(Car c) { 
     System.out.println(c + " parking at " + this); 
     //don't care how this is implemented, just keep in mind 
     //that it will enter in a loop until the owner came back. 
     occupied = false; 
    } 
} 

Se corro questo con tre auto, poi mi finirà ad avere car0 un parcheggio a spot1, car1 un parcheggio a spot0 , car2 è in attesa su spot0, perché car1 sta eseguendo il parco di blocchi sincronizzati (Car c). Non capisco come sia possibile che due auto possano parcheggiare nello stesso punto se il willingToPark è sincronizzato.

Grazie

+1

Grazie per la piacevole sorpresa. Quando ho visto il titolo e le frasi di apertura, non mi aspettavo una domanda ben formulata. –

+1

Penso che dovresti avere un riferimento in classe Spot per l'auto che si trova attualmente in quel punto, quindi quando il proprietario di questo spot vuole parcheggiare puoi avvisare l'auto che attualmente è parcheggiata che deve guidare e fare spazio per il proprietario. – Keammoort

+0

puoi mostrare il tuo metodo principale che esegue le auto? come si cambia stato auto? in base alle informazioni fornite non è ovvio il motivo per cui le auto sono bloccate con il metodo willingToPark – AdamSkywalker

risposta

6

non capisco come è possibile che due auto possono parcheggiare nello stesso posto se il willingToPark è sincronizzato.

In realtà è semplice. Car1 acquisisce spot0 e inizia ad attendere il proprietario in un ciclo all'interno del metodo park() (non hai fornito il codice). Mentre è in attesa, possiede il monitoraggio e non consente a nessuno di chiamare metodi sincronizzati su spot0.
Questo è il motivo per cui car2 si blocca sul metodo willingToPark().

+0

Ahhh quindi ho sbagliato il sincronizzato. Quindi ho bisogno di due blocchi, uno che è lo stesso spot per il metodo park e uno per il willingToPark –

+0

No, in realtà, non posso usare l'oggetto punto come blocco. –

2

Il problema è nel loop di park. Immagina:

  • Due fili afferrano lo stesso spot, s.
  • Entrambi cercano di acquisire il blocco del monitor su s; solo uno avrà successo
  • Quello che riesce causerà park in rotazione, mentre si tiene ancora il blocco.
  • L'altro attende pazientemente che il blocco venga rilasciato, in modo che possa provare ad acquisirlo.

Non c'è niente (a corto di uccidere la JVM) che dirà il thread che non è riuscito a parcheggiare, per smettere di aspettare su s.Attenderà fino a quando non sarà in grado di acquisire il blocco, operazione che si verificherà solo quando il primo thread termina il ciclo su park.

La soluzione non prevede il looping su park. Invece, la auto dovrebbe annullare il flag occupied tramite un nuovo metodo unpark(Car). Questo metodo deve anche essere sincronizzato, per garantire la visibilità della memoria tra i thread.

Ora il flusso dei dati si presenta come:

  • due thread sia afferrare lo stesso per il pranzo, s.
  • Entrambi cercano di acquisire il blocco del monitor su s; solo uno avrà successo
  • Quello che riesce set occupied = true, e subito ritorna e quindi rilascia il blocco su s
  • L'altro acquisisce la serratura s, e vede che il posto è occupato.

Per inciso, non è nemmeno necessario un metodo synchronized per questo. È possibile utilizzare il metodo compareAndSet di AtomicBoolean, che consente di controllare in modo atomico il valore di AtomicBoolean e impostarlo solo se il valore corrente è quello che si aspetta che sia. Pertanto, return occupied.compareAndSet(false, true) significa "controllare il valore corrente in modo atomico, se è falso, impostarlo su true e restituire true; se è vero, mantenerlo così com'è e restituire false". Questo tipo di comportamento è utile, ma un po 'più avanzato.

2

Flag i posti di deposito con un AtomicBoolean:

class Spot{ 
    public final AtomicBoolean flag = new AtomicBoolean(false); 
} 

qualche altro posto nel codice, c'è una gara tra i thread di auto per afferrare i punti:

if(spot.flag.compareAndSet(false,true)){ 
    // spot owned by current thread !! 
    // for other threads, `compareAndSet` will fail because they expect it to be `false`. 

    // visit store and buy stuff while car is parked. 

    // time to go, release the spot 
    spot.flag.set(false); 
}else{ 
    // find another spot 
} 

Esempio completa: http://ideone.com/dw3LnV

Questo approccio è in attesa libera e senza blocco, è possibile inserire logica di contestazione spot in un ciclo while e i thread continueranno a contendersi per il flag.

Ulteriori approfondimenti: http://www.ibm.com/developerworks/library/j-jtp11234/

1

Credo che non si ottiene il quadro generale sulla sincronizzazione, Luca. Il ciclo di attesa è il problema. Come per la sincronizzazione stessa, può essere fatto solo su oggetti:

  • aventi un metodo synchronized rende sincronizzati su this
  • avente un blocco di codice synchronized(someObject) {...} lo rende sincronizzato someObject

Quando un viene chiamato il metodo sincronizzato, non consente a nessun altro metodo sincronizzato di utilizzare l'oggetto su cui è sincronizzato. Quindi, usando quel ciclo, hai bloccato tutto il resto. Come altri hanno affermato, si dovrebbe usare solo la sincronizzazione per il più breve possibile per eseguire l'operazione.

È possibile, ad esempio, sincronizzare solo la parte rimuovere dal parcheggio nel metodo parco:

void park(Car c) { 
    System.out.println(c + " parking at " + this); 
    //don't care how this is implemented, just keep in mind 
    //that it will enter in a loop until the owner came back. 
    synchronized (this) { 
     occupied = false; 
    } 
} 
+0

Ciao grazie. Sì, questo è stato il mio incomprensibile! –

+0

Sentiti libero di contrassegnarlo come una risposta alla tua domanda se risponde. Adam ha evidenziato il problema con il loop, ma non penso che ti abbia dato una soluzione. – Vlasec

Problemi correlati