2013-02-13 5 views
6

Ho un amico che ha dichiarato che tutti i metodi statici dovrebbero essere synchronized nel contesto di un'applicazione Web Java. È vero? Ho letto molte altre pagine di overflow dello stack su questo. Che cosa sono venuto a credere è che solo è necessario sincronizzare se si dispone di:Java: tutti i metodi statici devono essere sincronizzati?

discussioni
  1. multipli (Come in un Sevlet Contenitore con un pool di thread)
  2. ClassLoader singolo
  3. dati condivisi tra le discussioni, sia si tratta di dati di sessione o dati statici membri.
  4. I dati condivisi devono essere modificabili. Leggere solo i dati è ok per condividere.

Sulla base di ciò, penso che i membri statici debbano essere sincronizzati, ma non i metodi statici.

import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

public class ThreadTest { 

    static String staticString = ""; 

    // This static method is safe b/c it only uses local data. 
    // It does not use any shared mutable data. 
    // It even uses a string builder. 
    static String safeStaticMethod(String in) { 
     // This also proves that StringBuilder is safe 
     // When used locally by a thread. 
     StringBuilder sb = new StringBuilder(); 
     sb.append("Hello: "); 
     sb.append(in); 
     return sb.toString(); 
    } 

    // This static method is not safe b/c it updates and reads 
    // shared mutable data among threads. 
    // Adding synchronized will make this safe. 
    static String unsafeStaticMethod(String in) { 
     staticString = in; 
     StringBuffer sb = new StringBuffer(); 
     sb.append("Hello: "); 
     sb.append(staticString); 
     return sb.toString(); 
    } 

    public static void main(String[] args) { 
     ThreadTest test = new ThreadTest(); 
     test.staticMethodWithLocalData(); 
     test.staticMethodWithStaticData(); 
    } 

    public void staticMethodWithLocalData() { 

     ExecutorService executor = Executors.newFixedThreadPool(2); 
     final int iterations = 100000; 

     executor.submit(new Runnable() { 

      @Override 
      public void run() { 
       for (int index = 0; index < iterations; ++index) { 
        if (!safeStaticMethod("Thread1").equals("Hello: Thread1")) { 
         System.out.println("safeStaticMethod at " + index); 
        } 
       } 
      } 
     }); 

     executor.submit(new Runnable() { 

      @Override 
      public void run() { 
       for (int index = 0; index < iterations; ++index) { 
        if (!safeStaticMethod("Thread2").equals("Hello: Thread2")) { 
         System.out.println("safeStaticMethod at " + index); 
        } 
       } 
      } 
     }); 
    } 

    public void staticMethodWithStaticData() { 

     ExecutorService executor = Executors.newFixedThreadPool(2); 
     final int iterations = 100000; 

     executor.submit(new Runnable() { 

      @Override 
      public void run() { 
       for (int index = 0; index < iterations; ++index) { 
        if (!unsafeStaticMethod("Thread1").equals("Hello: Thread1")) { 
         System.out.println("unsafeStaticMethod at " + index); 
        } 
       } 
      } 
     }); 

     executor.submit(new Runnable() { 

      @Override 
      public void run() { 
       for (int index = 0; index < iterations; ++index) { 
        if (!unsafeStaticMethod("Thread2").equals("Hello: Thread2")) { 
         System.out.println("unsafeStaticMethod at " + index); 
        } 
       } 
      } 
     }); 
    } 
} 

Questo codice dimostra il punto?

MODIFICA: Questo è solo un codice usa e getta che ho hackerato per dimostrare il punto.

+8

Scrivere codice sicuro per il thread è ** molto ** più complicato dello slapping 'synchronized' in posti casuali. – SLaks

+1

Come nota a margine, il 'safeStaticMethod' come scritto sarebbe comunque sicuro usando un' StringBuffer', poiché il buffer non è condiviso tra i thread. è locale a quella particolare chiamata al metodo. – Charlie

+1

Per elaborare quanto sopra: non ci sono fondamentalmente regole "se X allora Y" che siano universalmente valide per scrivere programmi sicuri per il thread. (La maggior parte di quelli che senti può finire per ridurre inutilmente la concorrenza per la tua app.) – millimoose

risposta

9

No, non tutti i metodi statici devono essere sincronizzati. La tua lista è sostanzialmente completa per quanto posso vedere. Prestare particolare attenzione quando il metodo statico sia

  1. accede a un membro statico che è mutevole, o
  2. viene passato un riferimento a un oggetto che può essere modificata.

Penso che sia ovvio che 1 (avere thread in primo luogo) è una condizione preliminare, poiché senza fili synchronize non ha senso.

Non ne ho mai sentito parlare 2, quindi non so se è una considerazione.

4

No, questo non è vero e sono sicuro che sarebbe dannoso. Non tutte le applicazioni devono essere concorrenti, e anche nelle applicazioni che devono essere concomitanti, non ogni pezzo di codice deve essere.

Come ulteriori prove, look at the source of String. Ci sono molti metodi statici in là, ma ho potuto trovare solo un metodo sincronizzato, e quello non è nemmeno statico.

+2

Per quanto String non sia sincronizzato, è perché String è immutabile - in gran parte perché gli oggetti immutabili sono di sola lettura e quindi _ intrinsecamente sicuri_ (punto 4 nella domanda originale). String è in realtà uno dei migliori esempi di una classe progettata esplicitamente per evitare l'uso di 'sincronizzati 'e comunque essere sicuri. – Matt

+2

Sì, è vero, lo ha indicato con il punto 4, ma la domanda è se i ** tutti i metodi statici devono essere sincronizzati e questo è un esempio che mostra "no, non lo fanno" –

+1

Oh, davvero. Capisco ora - buon esempio :) – Matt

2

I metodi statici dovrebbero quasi mai essere sincronizzati in una webapp. A meno che tu non sia sicuro al 100% che le uniche persone che utilizzeranno l'applicazione saranno il tuo team di contabilità di 3 persone, e saranno disposte ad essere rosse in faccia se decolleranno a livello aziendale e tutti i bruschi improvvisi si fermeranno quasi del tutto.

La creazione di una risorsa globale, di blocco e condivisa è un fallimento totale della scalabilità! Inoltre causerà un sacco di mal di testa e probabilmente ti bloccherà in una soluzione in stile Terracotta se dovessi aver bisogno di clusterare il server delle applicazioni.

1

In un'applicazione Web (come una build che utilizza il servlet/JSP), si dovrebbe sempre evitare di rendere un metodo sincronizzato poiché sfida l'intera filosofia dell'accessibilità multi-thread. Sul posto, cerca sempre di inserire l'unico codice necessario, a cui è necessario accedere uno per uno, all'interno del blocco sincronizzato.

1

Niente affatto. Per lo più, i metodi statici che ho incontrato non modificano alcuna variabile statica e quindi non richiedono di essere sincronizzati.

Per semplice comprensione,

//sample static util method to get string in upper case  
    public static String getName(String name){ 
     return a.toUpperCase(); 
    } 

Il metodo di cui sopra può essere chiamato da 1000s di fili e tuttavia sarà thread-safe perché il metodo richiede solo un argument- nome String e che è dallo Stack discussione. Non sono dati condivisi tra thread.

Pensateci, se tutti i metodi statici fossero sincronizzati, le applicazioni web devono essere estremamente lente e letargiche. Avremo il blocco a livello di classe ogni volta che un singolo thread tenta di accedere al metodo.

Esistono molti metodi statici nelle API fornite da JDK. Se tutti quelli fossero sincronizzati, sono abbastanza sicuro che non useremmo JAVA.

Nel suo caso, c'è un (variabile livello di classe) variabile statica che viene modificato dal metodo statico . Sì, se si creano più thread e si accederà al metodo statico, è possibile che si verifichi un'interferenza del thread. Non è thread-safe in quanto vi è dati condivisi tra di loro.

Principalmente, i metodi statici sono funzioni di utilità a seconda degli argomenti che vengono passati a loro.

Si noti che i metodi statici non sincronizzati sono thread-safe se non modificano le variabili di classe statiche.