我试图理解StringBuilder和StringBuffer之间的区别。下面这个程序的目标是让两个线程(Jack和Jill)竞争改变StringBuffer和StringBuilder的值。如果原始值已经被修改,那么线程将不会修改该变量。为什么我的StringBuffer变量没有同步。其中as StringBuilder变量的行为与synchronized相同
StringBuffer:Jack赢了StringBuilder:Jack wonJill赢了
其中实际输出是相反的。
public class StringBufferIsThreadSafe{
public static void main(String[] args) throws InterruptedException {
Hello hello = new Hello();
Thread jackThread = new Thread(new Jack(hello.strBuf, hello.strBuilder));
Thread jillThread = new Thread(new Jill(hello.strBuf, hello.strBuilder));
jackThread.start();
jillThread.start();
jackThread.join();
jillThread.join();
System.out.println("StringBuffer: "+hello.strBuf);
System.out.println("StringBuilder: "+hello.strBuilder);
}
}
class Jack implements Runnable{
private StringBuffer strBuf;
private StringBuilder strBuilder;
public Jack(StringBuffer strBuf, StringBuilder strBuilder) {
this.strBuf = strBuf;
this.strBuilder = strBuilder;
}
@Override
public void run() {
try {
Thread.sleep(3000);
if(this.strBuf.toString().equals("")) {
this.strBuf.append("Jack won");
}
if(this.strBuilder.toString().equals("")) {
this.strBuilder.append("Jack won");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Jill implements Runnable{
private StringBuffer strBuff;
private StringBuilder strBuilder;
public Jill(StringBuffer strBuff, StringBuilder strBuilder) {
this.strBuff = strBuff;
this.strBuilder = strBuilder;
}
@Override
public void run() {
try {
Thread.sleep(3000);
if(this.strBuff.toString().equals("")) {
this.strBuff.append("Jill won");
}
if(this.strBuilder.toString().equals("")) {
this.strBuilder.append("Jill won");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Hello {
StringBuffer strBuf = new StringBuffer();
StringBuilder strBuilder = new StringBuilder();
public Hello() {
this.strBuf.append("");
this.strBuilder.append("");
}
}
我想你误解了‘内部同步’的意思。
“内部同步”不是巫毒魔法。StringBuffder中的代码根本无法影响您的代码。当您写以下内容时,这将成为一个问题:
if (strBuffer.toString().equals("")) {
strBuffer.append("Jill won");
}
这里没有任何保证,具体来说,您似乎认为整个操作现在在某种程度上是原子的(在此之后,strBuffer将不可能包含Jack wonJill Won
)。但事实并非如此。完全有可能在这段代码之后,内容现在是Jack wonJill Won
。
这个问题有两种解决方案:
[1]显式使用锁
synchronized (strBuf) {
if (strBuf.toString().isEmpty()) strBuf.append("Jill won");
}
效果很好。这样,您实际上不再需要内部同步方面了。你已经搞定了。
因此,忘记StringBuffer
而只使用StringBuilder
的原因如下: