2010-06-26 16 views
6

Sono nuovo al multithreading e ho scritto questo codice che stampa i numeri 1-10000 eseguendo contemporaneamente i thread incrementando e stampando una variabile."sincrono" è davvero solo zucchero sintattico?

Ecco il codice che sto utilizzando:

package threadtest; 

public class Main{ 

    static int i=0; 
    static Object lock=new Object(); 

    private static class Incrementer extends Thread{ 

     @Override 
     public void run(){ 
      while (true){ 
       synchronized(lock){ 
         if (i>=10000) 
          break; 
         i++; 
         System.out.println(i); 
       } 
      }    
     } 
    } 


    public static void main(String[] args) { 
     new Incrementer().start(); 
     new Incrementer().start(); 
     new Incrementer().start(); 
     new Incrementer().start(); 
     new Incrementer().start(); 
     new Incrementer().start(); 
    } 
} 

Questo funziona - ho scritto un programma di test per verificare l'uscita, ed i numeri stampati sono esattamente 1-10000 in ordine.

La mia domanda è questa: ho sentito che synchronized è solo zucchero sintattico. Ma non riesco a ottenere un risultato positivo senza usarlo. Cosa mi manca?

+6

Certamente non lo è. Puoi citare le fonti in cui hai sentito questo? – BalusC

+1

@BalusC: vedere la risposta accettata. Penso che le fonti si riferissero semplicemente ai metodi sincronizzati * * come zucchero sintattico - non sono sicuro che sia esattamente corretto o meno, ma almeno non contraddice le mie scoperte come pensavo. @ Skaffman: avrei potuto sbagliare senza 'sincronizzato 'per tutto quello che sapevo :) – Cam

+0

@incrediman' synchronized' su un metodo è più o meno lo stesso di mettere il corpo del metodo in 'sincronizzato (this) {...} '(o per un metodo statico' synchronized (MyClass.class) {...} '). (Viene rappresentato in modo diverso nei file di classe, ma di solito non ti preoccupa.) –

risposta

14

synchronized non è affatto zucchero sintattico per niente. Non c'è modo di lavorare i blocchi in Java senza usare la parola chiave synchronized.

Dove c'è uno "zucchero sintattico" di un tipo nei blocchi in Java è che synchronized può essere applicato sia ai blocchi (come è stato fatto in precedenza) sia a metodi interi. I seguenti due metodi sono più o meno equivalenti nella semantica:

synchronized void method1() { 
    // ... do stuff ... 
} 

void method2() { 
    synchronized(this) { 
    // ... do stuff ... 
    } 
} 

Allora perché si vuole fare la seconda versione, invece del primo?

  • Le chiamate di metodo sincronizzate sono molto più lente delle normali invocazioni di metodo, come di circa un ordine di grandezza. Se il codice sincronizzato non è garantito per essere sempre eseguito (diciamo che è condizionale), probabilmente non si desidera sincronizzare l'intero metodo.
  • I metodi sincronizzati mantengono i blocchi più a lungo dei blocchi sincronizzati (a causa di tutto il metodo di installazione/codice di decompressione). Il secondo metodo sopra terrà il blocco per meno tempo perché il tempo impiegato per impostare e abbattere il frame dello stack non verrà bloccato.
  • È possibile avere un controllo più preciso su esattamente ciò che si sta bloccando se si va con i blocchi sincronizzati.
  • (Per gentile concessione di starblue) I blocchi sincronizzati possono utilizzare oggetti diversi da this per il blocco che offre una semantica di blocco più flessibile.
+0

Grazie, ha senso. Sono contento di aver chiarito :) – Cam

+0

Con un blocco è possibile utilizzare alcuni oggetti privati ​​come il blocco, che impedisce le interferenze da codice esterno. – starblue

+0

Buon punto. Lo aggiungerò alla risposta, starblue. –

1

Sembra che le tue fonti siano semplicemente sbagliate. La parola chiave syncrhonized è importante da utilizzare e utilizzare correttamente quando si scrive codice thread-safe. E sembra che i tuoi esperimenti lo dimostrino.

Per maggiori informazioni sulla sincronizzazione in Java:

Java Synchronized Methods

Java Locks and Synchronized Statements

0

sincronizzazione è uno dei concetti più importanti, mentre la programmazione in ambiente multi thread. Mentre si utilizza la sincronizzazione, è necessario considerare l'oggetto su cui avviene la sincronizzazione. Ad esempio se un metodo statico deve essere sincronizzato allora la sincronizzazione deve essere al livello classe utilizzando

synchronized(MyClass.class){ 
//code to be executed in the static context 
} 

se il blocco in metodo di istanza deve essere sincronizzato allora la sincronizzazione deve utilizzare un'istanza di un oggetto è condiviso tra tutti i thread. La maggior parte delle persone non funziona correttamente con il secondo punto come appare nel codice in cui la sincronizzazione sembra essere su oggetti diversi anziché su un singolo oggetto.

1

In realtà da Java 5 (formalmente) si dispone di un set di strumenti alternativo in java.util.concurrent. Vedi here per maggiori dettagli. Come dettagliato nell'articolo, il modello di blocco del monitor fornito a livello di linguaggio Java ha un numero di limitazioni significative e può essere difficile ragionare su quando ci sono un complesso insieme di oggetti interdipendenti e relazioni di blocco che rendono la possibilità di live-lock. La libreria java.util.concurrent offre semantica di blocco che potrebbe essere più familiare ai programmatori che hanno esperienza in sistemi POSIX

1

Ovviamente, "sincronizzato" è solo zucchero sintattico - estremamente utile zucchero sintattico.

Se vuoi programmi Java senza zucchero, si dovrebbe scrivere direttamente nel codice Java byte del monitorenter, monitorexit, serratura, e sbloccare operazioni fa riferimento in VM Specifications 8.13 Locks and Synchronization

Ci è un blocco associato a ogni oggetto. La lingua Java di programmazione non fornisce un modo per eseguire il blocco separato e sbloccare le operazioni ; invece, sono implicitamente eseguiti dai costrutti di livello alto che sistemano sempre le operazioni in modo corretto. (La macchina virtuale Java , tuttavia, fornisce separati monitorenter e monitorexit istruzioni che implementano il blocco e sbloccare operazioni.)

Il comando sincronizzato calcola un riferimento ad un oggetto; quindi tenta di eseguire un'operazione di blocco su quell'oggetto e non procede ulteriormente finché l'operazione di blocco non ha completato . (Un'operazione blocco può essere ritardata poiché le regole circa serrature possono impedire il principale memoria di partecipare fino a qualche altro filo è pronto per eseguire uno o più operazioni di sblocco.) Dopo l'operazione serratura è stata eseguita, il il corpo dell'istruzione sincronizzata è eseguito. Normalmente, un compilatore per il linguaggio programmazione Java assicura che l'operazione di blocco attuato da un'istruzione monitorenter eseguita prima dell'esecuzione del corpo del dichiarazione sincronizzato è abbinato da un'operazione di sblocco implementato da un'istruzione monitorexit ogniqualvolta l'istruzione sincronizzata completa, se il completamento è normale o brusco.

Un metodo sincronizzato automaticamente esegue un'operazione di blocco quando è invocato il numero ; il suo corpo non viene eseguito finché l'operazione di blocco non ha completato . Se il metodo è un metodo di istanza, si blocca la serratura associato con l'istanza per cui è stato richiamato (ovvero, l'oggetto che sarà noto come questo durante l'esecuzione del corpo del metodo). Se il metodo è statico, lo blocca il blocco associato all'oggetto classe che rappresenta la classe in cui è definito il metodo. Se l'esecuzione di del corpo del metodo è sempre completata, o normalmente , un'operazione di sblocco è eseguita automaticamente sullo stesso blocco .

Migliore pratica è che se una variabile è mai essere assegnato da un filo e usato o assegnato da un altro, poi tutti accessi a quella variabile deve essere racchiuso in metodi sincronizzati o dichiarazioni sincronizzati.

Anche se un compilatore per il linguaggio di programmazione Java normalmente garanzie uso strutturato di serrature (vedere Sezione 7.14, "Sincronizzazione"), non v'è alcuna garanzia che tutto il codice sottoposto alla macchina virtuale Java sarà obbedire a questo proprietà. Le implementazioni della macchina Java virtuale sono consentite ma non richieste per applicare entrambe le seguenti due regole che garantiscono il blocco strutturato.

Sia T un thread e L un blocco. Poi:

  1. Il numero di operazioni di blocco eseguite da T su L durante un metodo chiamata deve essere uguale al numero di sbloccare operazioni eseguite da T su L durante l'invocazione del metodo se il metodo invocazione completa normalmente o bruscamente.

  2. In nessun momento durante una chiamata di metodo possono il numero di sblocco operazioni eseguite da T su L dal l'invocazione del metodo superare il numero di operazioni serratura eseguita da T su L poiché il metodo.

In termini meno formali, durante un procedimento invocazione ogni operazione di sblocco su L deve corrispondere qualche precedente blocco funzionamento su L.

nota che il bloccaggio e sbloccaggio automaticamente eseguita dal Java la macchina virtuale quando si richiama un metodo sincronizzato viene considerata per durante il richiamo del metodo di chiamata .

Problemi correlati