Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 从非同步方法访问的同步对象_Java_Multithreading_Synchronization - Fatal编程技术网

Java 从非同步方法访问的同步对象

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

假设我有一个类,其中有一个声明为成员变量的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方法时,我得到了第二个输出


所以我知道,即使我使用同步类,我也必须同步使用同步类的块/方法。我说得对吗?

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啊,我明白了,很公平,您也是正确的