假设我有一个类,其中有一个StringBuffer声明为成员变量。两个线程试图像下面那样操纵对象
public class SomeService {
private StringBuffer sb = new StringBuffer();
public void printName(String name) {
sb.append(name);
System.out.println(sb);
}
}
public class StringBufferSynchronizationTest implements Runnable {
private SomeService service = new SomeService();
public StringBufferSynchronizationTest() {
Thread thread = new Thread(this);
thread.start();
}
public static void main(String[] args) {
new StringBufferSynchronizationTest().service.printName("oops");
}
@Override
public void run() {
service.printName("java");
}
}
我得到了这个输出
oopsjava
oopsjava
我以为我会得到
oops
oopsjava
作为输出。当我同步printName方法时,我得到了第二个输出。
所以我明白,即使我使用同步类,我也必须同步使用synchronized类的块/方法。我是对的吗?
答案 0 :(得分:1)
StringBuffer保证只有一个线程可以输入append或同一实例的任何其他方法。但这就是全部,没有更多的保证。
答案 1 :(得分:1)
是的,StringBuffer是同步的,如果你想要预期的结果,也同步函数printName。可以在sb.append(name);
和System.out.println(sb);
之间进行上下文切换,尤其是与缓慢的IO相关。
public synchronized void printName(String name) {
sb.append(name);
System.out.println(sb);
}
答案 2 :(得分:1)
这取决于你想要完成的任务。
让我们看看printName
public void printName(String name) {
sb.append(name);
System.out.println(sb);
}
由于sb在synchronized中,sb.append(name)
只有1个线程在对象上运行可变状态。这可以防止在您的示例中使用值
oojavaps
ojopsava
等等。但是,由于您的printName方法未同步,因此无法保证在2个线程中调用printName中的2个方法的顺序。
理解它的最简单方法可能是提出导致输出的执行顺序
oopsjava
oopsjava
最初sb
是空字符串
假设主线程执行sb.append(name)
,sb
保留oops
,但在执行println
之前它被抢占
构造函数线程接管并执行整个方法,首先将java
附加到sb
以获取oopsjava
中的sb
,然后打印输出,以获取
oopsjava
然后执行主线程,打印sb
以获取
oopsjava
我在这里有一个输出示例不正确,@ Cruncher在评论中指出了这一点并将其删除。