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?
- multipli (Come in un Sevlet Contenitore con un pool di thread)
- ClassLoader singolo
- dati condivisi tra le discussioni, sia si tratta di dati di sessione o dati statici membri.
- 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.
Scrivere codice sicuro per il thread è ** molto ** più complicato dello slapping 'synchronized' in posti casuali. – SLaks
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
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