Java 从非同步方法访问的同步对象
假设我有一个类,其中有一个声明为成员变量的StringBuffer。两个线程正试图操纵对象,如下所示Java 从非同步方法访问的同步对象,java,multithreading,synchronization,Java,Multithreading,Synchronization,假设我有一个类,其中有一个声明为成员变量的StringBuffer。两个线程正试图操纵对象,如下所示 public class SomeService { private StringBuffer sb = new StringBuffer(); public void printName(String name) { sb.append(name); System.out.println(sb); } } public class
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方法时,我得到了第二个输出
所以我知道,即使我使用同步类,我也必须同步使用同步类的块/方法。我说得对吗?StringBuffer保证只有一个线程可以输入同一实例的append或任何其他方法。但仅此而已,没有更多的保证。是的,StringBuffer是同步的,如果您想要预期的结果,还同步了函数printName。上下文切换可以在
sb.append(name)之间发生代码>和系统输出打印LN(sb)代码>特别是与慢速IO相关
public synchronized void printName(String name) {
sb.append(name);
System.out.println(sb);
}
这取决于你想完成什么
让我们看看printName
public void printName(String name) {
sb.append(name);
System.out.println(sb);
}
由于sb处于synchronized状态,sb.append(name)
只有一个线程在对象可变状态下运行。在您的示例中,这可以防止值的字符串
oojavaps
ojopsava
等等。但是,由于printName方法未同步,因此无法保证printName中两个方法跨2个线程的调用顺序
理解它的最简单方法可能是提出导致输出的执行顺序
oopsjava
oopsjava
最初sb
是空字符串
假设主线程执行sb.append(name)
,将sb
留给oops
,但是它在执行println
之前被抢占
构造函数线程接管并执行整个方法,首先将java
附加到sb
以在sb
中获取oopsjava
,然后打印输出以获取
oopsjava
然后执行主线程,打印sb
以获取
oopsjava
@Cruncher在评论中指出,我这里有一个输出不正确的例子,我将其删除。是的,你必须使用同步方法,首先你必须知道你想要什么,然后你开始思考如何获得它。StringBuffer
上的每个方法调用都是独立同步的,这显然与单独方法调用的交错无关。我相信,如果运行足够多的时间,最终将获得预期的输出。这是一个竞争条件。那么,拥有同步的StringBuffer有什么用呢。我可以同步代码中的块/方法。这是一样的,对吧?@Cruncher是的,我也相信同样的道理。但我希望输出总是与第二个类似。好吧,它还保证如果两个线程都在同一个实例上调用append
(或其他同步的StringBuffer
方法),那么将出现一个before关系。:-)StringBuffer保证其方法以顺序一致的方式执行。这包括互斥和以前发生的情况。在您的示例中,这可以防止值的字符串
——实际上,它可以防止比这更严重的问题,比如破坏StringBuffer
的内部不变量。这样的事情是非同步方法调用的合法结果。我不认为oops\njava
是一种可能的结果。StringBuffer已同步,您可以追加。它无法同时通过append调用和“删除”代码中的第一个append。@MarkoTopolnik you comments没有抓住要点。我不打算列出所有可能的错误模式,因为StringBuffer没有同步。我在你的观点之外提出我自己的观点,因为我认为这一点对于理解数据竞争是什么很重要。对代码进行推理,同时只考虑您指出的轻微情况,这将导致不同步和代码损坏。@MarkoTopolnik啊,我明白了,很公平,您也是正确的