FULL PRODUCT VERSION :
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
Java HotSpot(TM) Server VM (build 21.0-b17, mixed mode)
The problem also exists in:
java version "1.6.0_27"
Java(TM) SE Runtime Environment (build 1.6.0_27-b07)
Java HotSpot(TM) Server VM (build 20.2-b06, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Linux 2.6.38-2-686-bigmem #1 SMP i686 GNU/Linux
EXTRA RELEVANT SYSTEM CONFIGURATION :
Intel(R) Core(TM)2 Duo CPU E8500 @ 3.16GHz
A DESCRIPTION OF THE PROBLEM :
StringBuffer's insert() method is not thread-safe when the receiver object is passed as the source sequence. As a result, concurrently calling a StringBuffer instance can give behavior not possible during any sequential ordering of the same calls. This contradicts the thread safety guarantee specified in the API documentation.
The documentation says: "Whenever an operation occurs involving a source sequence (such as appending or inserting from a source sequence) this class synchronizes only on the string buffer performing the operation, not on the source. "
This sentence doesn't consider the case where the string buffer performing the operation is the same as the source sequence. In this case, the implementation performs parts of the insert operation (a call to s.length()) without any synchronization, leading to unexpected, non-thread-safe behavior.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See the test case below.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The code should run without any output.
ACTUAL -
The code throws an IndexOutOfBoundsException.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "Thread-6526" java.lang.IndexOutOfBoundsException: start 0, end 3, s.length() 2
at java.lang.AbstractStringBuilder.insert(AbstractStringBuilder.java:1104)
at java.lang.StringBuffer.insert(StringBuffer.java:478)
at java.lang.StringBuffer.insert(StringBuffer.java:468)
at newbugs.StringBufferTest$1.run(StringBufferTest.java:14)
at java.lang.Thread.run(Thread.java:662)
REPRODUCIBILITY :
This bug can be reproduced occasionally.
---------- BEGIN SOURCE ----------
package newbugs;
public class StringBufferTest {
public static void main(String[] args) throws Exception {
new StringBufferTest().run();
}
private void run() throws Exception {
for (int i = 0; i < 10000000; i++) {
final StringBuffer sb = new StringBuffer("abc");
Thread t1 = new Thread(new Runnable() {
public void run() {
sb.insert(1, sb);
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
sb.deleteCharAt(0);
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException ie) {}
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Don't pass a string buffer to itself when using multiple threads.
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
Java HotSpot(TM) Server VM (build 21.0-b17, mixed mode)
The problem also exists in:
java version "1.6.0_27"
Java(TM) SE Runtime Environment (build 1.6.0_27-b07)
Java HotSpot(TM) Server VM (build 20.2-b06, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Linux 2.6.38-2-686-bigmem #1 SMP i686 GNU/Linux
EXTRA RELEVANT SYSTEM CONFIGURATION :
Intel(R) Core(TM)2 Duo CPU E8500 @ 3.16GHz
A DESCRIPTION OF THE PROBLEM :
StringBuffer's insert() method is not thread-safe when the receiver object is passed as the source sequence. As a result, concurrently calling a StringBuffer instance can give behavior not possible during any sequential ordering of the same calls. This contradicts the thread safety guarantee specified in the API documentation.
The documentation says: "Whenever an operation occurs involving a source sequence (such as appending or inserting from a source sequence) this class synchronizes only on the string buffer performing the operation, not on the source. "
This sentence doesn't consider the case where the string buffer performing the operation is the same as the source sequence. In this case, the implementation performs parts of the insert operation (a call to s.length()) without any synchronization, leading to unexpected, non-thread-safe behavior.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See the test case below.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The code should run without any output.
ACTUAL -
The code throws an IndexOutOfBoundsException.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "Thread-6526" java.lang.IndexOutOfBoundsException: start 0, end 3, s.length() 2
at java.lang.AbstractStringBuilder.insert(AbstractStringBuilder.java:1104)
at java.lang.StringBuffer.insert(StringBuffer.java:478)
at java.lang.StringBuffer.insert(StringBuffer.java:468)
at newbugs.StringBufferTest$1.run(StringBufferTest.java:14)
at java.lang.Thread.run(Thread.java:662)
REPRODUCIBILITY :
This bug can be reproduced occasionally.
---------- BEGIN SOURCE ----------
package newbugs;
public class StringBufferTest {
public static void main(String[] args) throws Exception {
new StringBufferTest().run();
}
private void run() throws Exception {
for (int i = 0; i < 10000000; i++) {
final StringBuffer sb = new StringBuffer("abc");
Thread t1 = new Thread(new Runnable() {
public void run() {
sb.insert(1, sb);
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
sb.deleteCharAt(0);
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException ie) {}
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Don't pass a string buffer to itself when using multiple threads.