2014-07-09 14 views
5

Ho scritto un programma in cui sono in esecuzione due thread, ecco perché ho utilizzato la parola chiave sincronizzata. Il mio codice è sotto-perché il metodo sincronizzato non funziona per il multithread

public class TestThreadAnother { 
    public synchronized void decrement(){ 
     try { 
      for (int i = 4; i > 0; i--) { 
       System.out.println("Thread " + i); 
      } 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 


public class TestThreadClass extends Thread { 
    private String threadName; 
    TestThreadAnother obj1; 

    public TestThreadClass(String threadName, TestThreadAnother Obj1) { 
     this.threadName = threadName; 
     obj1 = Obj1; 
     System.out.println("Creating " + threadName); 
    } 

    @Override 
    public void run() { 
     System.out.println("Running " + threadName); 
     obj1.decrement(); 
     System.out.println("End of " + threadName); 
    }  
} 

public class TestThreadMain { 

    public static void main(String[] args) { 
     TestThreadAnother obj1 = new TestThreadAnother(); 

     TestThreadClass t1 = new TestThreadClass("Thread 1", obj1); 
     TestThreadClass t2 = new TestThreadClass("Thread 2", obj1); 

     t1.start(); 
     t2.start(); 
    } 
} 

L'uscita shoud essere -

Creating Thread 1 
Creating Thread 2 
Running Thread 1 
Thread 4 
Thread 3 
Thread 2 
Thread 1 
End of Thread 1 
Running Thread 2 
Thread 4 
Thread 3 
Thread 2 
Thread 1 
End of Thread 2 

Ma sto ottenendo il seguente risultato-

Creating Thread 1 
Creating Thread 2 
Running Thread 2 
Thread 4 
Thread 3 
Running Thread 1 
Thread 2 
Thread 1 
Thread 4 
Thread 3 
Thread 2 
Thread 1 
End of Thread 1 
End of Thread 2 

ma la mia domanda è come io sto usando metodo sincronizzato poi perché questo sta accadendo? Come sapere se io uso sincronizzato, allora al primo thread verrà eseguito e poi verrà eseguito il secondo. Ma nel mio caso a volte questo non sta accadendo. Gli esperti Java hanno bisogno del tuo aiuto per quanto riguarda questo problema.

+0

solo dopo Thread2 completato il metodo decremento, Thread1 eseguito lo stesso metodo-perché solo il metodo è sincronizzato –

+0

Cosa si aspetta il try/catch in 'decremento()' per catturare? –

risposta

11

L'uscita è corretta. Solo il tuo metodo decrement() è sincronizzato. Ciò significa che il metodo decrement() di questo oggetto non può essere chiamato da più di un thread alla volta. Ma ciò non impedisce ad altri thread di fare qualcos'altro mentre un thread è in esecuzione decrement().

L'output mostra che, mentre filo 2 sta eseguendo decrement(), filo 1 sta eseguendo la prima System.out.println() nel metodo run(), poi eventualmente attende filo 2 ad aver completato la chiamata decrement(), poi esegue decrement().

+0

Ma se voglio la mia uscita proprio come la prima, allora cosa dovrei fare? – user3732316

+0

Inserisci le due chiamate System.out.println() del metodo run() nel metodo decrement(). O dovresti condividere un singolo Runnable tra i tuoi due thread e avere il suo metodo run() sincronizzato. O si sincronizzerebbe esplicitamente l'intero corpo del metodo run() su un oggetto di blocco condiviso. Ciò non garantirebbe comunque che thread1 inizi prima di thread2, comunque. –

+0

@ user3732316 Provalo come ho detto nella mia risposta. – gprathour

3

Si dovrebbe fare in questo modo,

@Override 
public void run() { 
    synchronized(obj1){ 
      System.out.println("Running " + threadName); 
      obj1.decrement(); 
      System.out.println("End of " + threadName); 
    } 

}  

E metodo può essere non sincronizzata

public void decrement(){ 
    try { 
     for (int i = 4; i > 0; i--) { 
      System.out.println("Thread " + i); 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 
+0

+1 ora produrrà l'output che l'OP si aspetta. –

+0

Se ho sincronizzato obj1, non è ancora sicuro che ogni volta che il thread 1 verrà eseguito per primo e poi il thread 2.perché l'o/p qualche volta il thread 1 eseguirà prima e poi il thread 2 e talvolta viceversa. – user3732316

+0

@ user3732316 - sì .. hai ragione ... non c'è alcuna garanzia. Cosa puoi fare, usa 'thread # join'. – TheLostMind

5

La cosa importante che si deve capire è. Synchronization fornisce solo la garanzia che due thread non mantengano il blocco sullo stesso oggetto nello stesso momento. Nel tuo caso, un thread (thread2 o thread1) otterrà il blocco e chiamerà il metodo decrement(). Fino a quando questo thread finisce, l'altra threda verrà bloccata.

Ora, analizzando l'output:

Creating Thread 1 
Creating Thread 2 
Running Thread 2 // Thread -2 has the lock now. 
Thread 4   // thrad-2's decrement() 
Thread 3 
Running Thread 1 // thread-1 is blocked. So, only this line is printed 
Thread 2   // still thread2 
Thread 1  // still thread2 
Thread 4  // now thread1 decrement() starts 
Thread 3 
Thread 2 
Thread 1 
End of Thread 1 
End of Thread 2 

stampa il thread.getName() nel metodo decremento per ottenere una migliore idea.

2

Questo è perché seguenti due righe non sono in blocco sincronizzato:

System.out.println("Running " + threadName); 

System.out.println("End of " + threadName); 

Così dopo e prima decremento filo() può essere commutato. Se si desidera che le osservazioni attesi è possibile modificare il metodo decremento come di seguito: metodo run

public synchronized void decrement(String threadName){ 
     System.out.println("Decrement: " + threadName); 
     try { 
      for (int i = 4; i > 0; i--) { 

       System.out.println("Thread " + i); 
      } 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     System.out.println("End Decrement: " + threadName); 
    } 

cambiamento come qui di seguito,

public void run() { 
    obj1.decrement(threadName); 

} 
+0

Ancora non si garantisce che il mio thread 1 verrà eseguito prima e poi infilare 2, perché a volte mi sto Discussione -Creazione 1 Creazione Thread 2 esecuzione Thread 2 Discussione 4 Discussione Thread 2 filettatura 1 Fine Decremento Discussione: 2 esecuzione thread 1 Discussione 4 Discussione thread 2 filettatura 1 Fine di decremento: thread 1 – user3732316

+0

Sì, questo non è mai garantita. Thread Scheduler è libero di scegliere per primo qualsiasi thread eseguibile dal pool di thread. Se si desidera eseguire prima il thread t1, potrebbe essere necessario inserire Thread.sleep dopo aver avviato Thread t1. – dips

0

Beh visto System.out.println di "Running ..." e "Fine corsa .. ."E decrement() metodo non sono in un'operazione atomica

Se si vuole attaccare con il metodo sincronizzato:

appena messo il

System.out.println("Running " + threadName); 

e

System.out.println("End of " + threadName); 

all'interno del vostro metodo di decrement() , quindi penso che sarai in grado di ottenere l'output desiderato

2

Le altre risposte hanno già spiegato perché il synchronized non funzionava come previsto. Questo è più di un follow-up sul commento:

Ancora si può garantire che il mio thread 1 verrà eseguito prima e poi infilare 2

È possibile gestire questo esternamente i fili utilizzando join sul primo:

t1.start(); // Start t1 
t1.join(); // Wait for t1 to complete 
t2.start(); // Start t2 

Se è necessario il thread principale per mantenere l'esecuzione, mentre i fili corrono uno alla volta, è possibile creare un thread manager per eseguirli. Fortunatamente, qualcuno già did this for us:

ExecutorService executor = Executors.newSingleThreadExecutor(); 
executor.execute(t1); 
executor.execute(t2); 

public static ExecutorService newSingleThreadExecutor()

Crea un esecutore che utilizza un singolo thread di lavoro operano fuori una coda infinita . (Si noti tuttavia che se questo thread singolo termina a causa di un errore durante l'esecuzione prima dello spegnimento, uno nuovo occuperà il posto se necessario per eseguire le attività successive.) Le attività sono garantite per l'esecuzione in sequenza e non più di una attività sarà attivo in qualsiasi momento.

(sottolineatura mia)

Problemi correlati