2013-05-03 16 views
7

Sto cercando di capire il meccanismo di sicurezza del thread in Java e ho bisogno di aiuto. Ho una classe:Variabile globale sicura thread in Java

public class ThreadSafe { 

    private Executor executor = new ScheduledThreadPoolExecutor(5); 

    private long value = 0; 

    public void method() { 
     synchronized (this) { 
      System.out.println(Thread.currentThread()); 
      this.value++; 
     } 
    } 

    private synchronized long getValue() { 
     return this.value; 
    } 

    public static void main(String... args) { 
     ThreadSafe threadSafe = new ThreadSafe(); 
     for (int i = 0; i < 10; i++) { 
      threadSafe.executor.execute(new MyThread()); 
     } 

    } 

    private static class MyThread extends Thread { 

     private ThreadSafe threadSafe = new ThreadSafe(); 

     private AtomicBoolean shutdownInitialized = new AtomicBoolean(false); 

     @Override 
     public void run() { 
      while (!shutdownInitialized.get()) { 
       threadSafe.method(); 
       System.out.println(threadSafe.getValue()); 
      } 
     } 
    } 

} 

Qui sto cercando di fare il filo value sicura, a cui accedere da un solo thread alla volta. Quando eseguo questo programma, vedo che c'è più di un thread che funziona su value anche se lo avvolgo nel blocco synchronized. Naturalmente questo ciclo sarà infinito ma è solo un esempio, sto fermando manualmente questo programma dopo pochi secondi, in modo da avere:

2470 
Thread[pool-1-thread-3,5,main] 
2470 
Thread[pool-1-thread-5,5,main] 
2470 
Thread[pool-1-thread-2,5,main] 

Diversi fili accedono e cambiare questo value. Qualcuno può spiegarmi perché è così? E come rendere sicuro questo thread variabile globale?

+0

non era sua intenzione di rendere lunga statica in questo esempio? Perché in ogni altro caso non esiste un problema di sicurezza del thread. –

+0

Assicurati di notare la mia risposta. La tua classe 'MyThread' dovrebbe _non_ estendere' Thread' e _should_ implementa 'Runnable'. – Gray

risposta

10

Ogni thread ha il proprio ThreadSafe e ogni ThreadSafe ha il proprio, diverso value. Inoltre, i metodi synchronized si bloccano su this, quindi ogni ThreadSafe si blocca su se stesso e nessuno di questi viene condiviso tra i thread. Si chiama thread-locality ed è il modo più semplice per garantire la sicurezza dei thread. :)

Per ottenere l'esperimento che ritengo necessario, è necessario modificare MyThread in modo tale che il suo costruttore utilizzi l'argomento ThreadSafe (invece di costruirne uno). Quindi, avere il metodo principale creare uno ThreadSafe e darlo a ciascuno MyThread in fase di costruzione.

4

Si ottiene lo stesso valore ogni volta poiché ciascuno dei tuoi Runnable s ha la propria istanza della classe ThreadSafe.

Se si desidera che tutti condividano la stessa classe, è necessario disporre di una sola istanza di ThreadSafe e passarla in tutti i lavori, vedere di seguito. Come accennato, un AtomicLong è la strada da percorrere se si desidera una condivisione protetta da thread long.

Inoltre, la classe deve essere nonextend Thread. Dovrebbe invece implements Runnable. Il tuo codice funziona perché Thread già implements Runnable. Se hai fatto myThread.interrupt(), in realtà non interromperebbe il thread perché sono i thread del pool di thread che stanno chiamando il tuo metodo run().

qualcosa come il seguente dovrebbe funzionare:

ThreadSafe threadSafe = new ThreadSafe(); 
for (int i = 0; i < 10; i++) { 
    threadSafe.executor.execute(new MyRunnable(threadSafe)); 
} 
... 
private static class MyRunnable implements Runnable { 
    private final ThreadSafe threadSafe; 
    public MyRunnable(ThreadSafe threadSafe) { 
     this.threadSafe = threadSafe; 
    } 
    ...