La domanda che sto chiedendo è relativa a Difference between StringBuilder and StringBuffer ma non uguale. Voglio vedere cosa succede realmente se uno StringBuilder viene modificato da due thread contemporaneamente.StringBuilder modificato da più thread
ho scritto le seguenti classi:
public class ThreadTester
{
public static void main(String[] args) throws InterruptedException
{
Runnable threadJob = new MyRunnable();
Thread myThread = new Thread(threadJob);
myThread.start();
for (int i = 0; i < 100; i++)
{
Thread.sleep(10);
StringContainer.addToSb("a");
}
System.out.println("1: " + StringContainer.getSb());
System.out.println("1 length: " + StringContainer.getSb().length());
}
}
public class MyRunnable implements Runnable
{
@Override
public void run()
{
for (int i = 0; i < 100; i++)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
StringContainer.addToSb("b");
}
System.out.println("2: " + StringContainer.getSb());
System.out.println("2 length: " + StringContainer.getSb().length());
}
}
public class StringContainer
{
private static final StringBuffer sb = new StringBuffer();
public static StringBuffer getSb()
{
return sb;
}
public static void addToSb(String s)
{
sb.append(s);
}
}
Inizialmente ho tenuto uno StringBuffer nel StringContainer. Poiché StringBuffer è thread-safe, alla volta, solo un thread possibile aggiungere ad esso, quindi l'uscita è coerente - o entrambi i fili riportata la lunghezza del buffer come 200, come:
1: abababababababababbaabababababababbaababababababababababababbabaabbababaabbaababababbababaabbababaabababbaabababbababababaababababababababbababaabbaababbaababababababbaababbababaababbabaabbababababaab
1 length: 200
2: abababababababababbaabababababababbaababababababababababababbabaabbababaabbaababababbababaabbababaabababbaabababbababababaababababababababbababaabbaababbaababababababbaababbababaababbabaabbababababaab
2 length: 200
o uno di essi segnalati 199 e l'altro 200, come:
2: abbabababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab
2 length: 199
1: abbababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababa
1 length: 200
la chiave è che l'ultimo filo per completare i rapporti una lunghezza di 200.
Ora, ho cambiato StringContainer avere uno StringBuilder invece di StringBuffer cioè
public class StringContainer
{
private static final StringBuilder sb = new StringBuilder();
public static StringBuilder getSb()
{
return sb;
}
public static void addToSb(String s)
{
sb.append(s);
}
}
Mi aspetto che alcune delle scritture vengano sovrascritte, il che sta accadendo. Ma il contenuto del StringBuilder e le lunghezze non corrispondono a volte:
1: ababbabababaababbaabbabababababaab
1 length: 137
2: ababbabababaababbaabbabababababaab
2 length: 137
Come si può vedere il contenuto stampato ha solo 34 caratteri, ma la lunghezza è 137. Perché succede questo?
@Extreme Coders - ho appena fatto un altro test di corsa:
2: ababbabababaabbababaabbababaababaabbaababbaaababbaabbabbabbabababbabababbbabbbbbabababbaabababbabaabaaabaababbaabaababababbaabbbabbbbbababababbababaab
1: ababbabababaabbababaabbababaababaabbaababbaaababbaabbabbabbabababbabababbbabbbbbabababbaabababbabaabaaabaababbaabaababababbaabbbabbbbbababababbababaab
1 length: 150
2 length: 150
versione Java: 1.6.0_45 e sto usando la versione di Eclipse: Eclipse IDE Java EE per gli sviluppatori web. Versione: Juno Service Release 2 Corporatura ID: 20130225-0426
UPDATE 1: mi sono imbattuto questa eclissi fuori e ora sembrano essere di corrispondenza, ma io sono sempre ArrayIndexOutOfBoundsException volte:
$ java -version
java version "1.6.0_27"
OpenJDK Runtime Environment (IcedTea6 1.12.5) (6b27-1.12.5-0ubuntu0.12.04.1)
OpenJDK Server VM (build 20.0-b12, mixed mode)
$ java ThreadTester
1: ababbbbbabbabababababaababbaabbbaabababbbababbabababbabbababbbbbbabaabaababbbbbbabbbbbaabbaaabbbbaabbbababababbbbabbababab
1 length: 123
2: ababbbbbabbabababababaababbaabbbaabababbbababbabababbabbababbbbbbabaabaababbbbbbabbbbbaabbaaabbbbaabbbababababbbbabbababab
2 length: 123
$ java ThreadTester
2: abbabaabbbbbbbbbababbbbbabbbabbbabaaabbbbbbbabababbbbbbbbbabbbbbbbababababbabbbbaabbbaaabbabaaababaaaabaabbaabbbb
2 length: 115
1: abbabaabbbbbbbbbababbbbbabbbabbbabaaabbbbbbbabababbbbbbbbbabbbbbbbababababbabbbbaabbbaaabbabaaababaaaabaabbaabbbb
1 length: 115
$ java ThreadTester
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at java.lang.String.getChars(String.java:862)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:408)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at StringContainer.addToSb(StringContainer.java:14)
at ThreadTester.main(ThreadTester.java:14)
2: abbbbbbababbbbabbbbababbbbaabbabbbaaabbbababbbbabaabaabaabaaabababaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
2 length: 114
Anche l'ArrayIndexOutOfBoundsException si sta verificando durante l'esecuzione da eclissi.
UPDATE 2: Ci sono due problemi che si verificano. Il primo problema del contenuto di StringBuilder che non corrisponde alla lunghezza sta accadendo solo in Eclipse e non quando corro in linea di comando (almeno 100 volte in cui l'ho eseguito su riga di comando non è mai successo).
Il secondo problema con ArrayIndexOutOfBoundsException deve riguardare l'implementazione interna della classe StringBuilder, che mantiene un array di caratteri e fa un Arrays.copyOf
quando espande la dimensione. Ma mi batte ancora come sta avvenendo una scrittura prima che la dimensione venga espansa, indipendentemente dall'ordine di esecuzione.
BTW, sono propenso a concordare con la risposta di @ GreyBeardedGeek che tutto questo esercizio è un'enorme perdita di tempo :-). A volte arriviamo a vedere solo i sintomi, cioè l'output di qualche codice e ci chiediamo cosa non va.Questa domanda ha dichiarato a priori che due thread stanno modificando un oggetto non sicuro (molto noto) thread.
UPDATE 3: Questa è la risposta ufficiale da Java Concurrency in Practice p. 35:
In mancanza di sincronizzazione, il compilatore, processore e runtime può fare alcune cose decisamente strane all'ordine in cui operazioni sembrano eseguire. Tenta di ragionare sull'ordine in quali azioni di memoria "devono" verificarsi in programmi con multithreading non sincronizzati in modo insufficiente sarà quasi certamente errato.
Il ragionamento su programmi concorrenti non sincronizzati in modo insufficiente è proibitivo proibitivo .
C'è anche un bell'esempio NoVisibility
nel libro a pag. 34.
Questo è strano, sulla mia macchina stampa '199' e' 200' e la dimensione di 'StringBuilder' è uguale al numero dei caratteri. L'ho eseguito più volte ma ottenendo sempre gli stessi risultati –
Anche questo ha qualcosa a che fare con l'IDE. Inizialmente ho usato IntelliJ e dava sempre lunghezze di '199' e' 200'. Successivamente ho eseguito questo in BlueJ e ha dato una lunghezza variabile per ogni corsa. Sembra che IntelliJ sia più bravo a gestire thread multipli rispetto ad altri IDE. –
c'è qualche possibilità di ottenere risultati dall'ID di riga di comando? :) –