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

时间:2014-02-03 13:43:08

标签: 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 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类的块/方法。我是对的吗?

3 个答案:

答案 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在评论中指出了这一点并将其删除。