Java同步阻止意外行为

时间:2014-12-30 13:00:24

标签: java multithreading synchronized

我有以下代码:

public class Experimenter implements Runnable
{
    private volatile Integer a = new Integer(0);

    public Experimenter() throws Exception
    {
        System.out.println("start");
    }

    public void funk() throws InterruptedException
    {
        synchronized (a)
        {
            System.out.println("in");
            Thread.sleep(5000);
            System.out.println("out");
        }
    }

    public static void main(String[] args) throws Exception
    {
        Thread a = new Thread(new Experimenter(), "a");
        Thread b = new Thread(new Experimenter(), "b");
        Thread c = new Thread(new Experimenter(), "c");
        Thread d = new Thread(new Experimenter(), "d");
        Thread e = new Thread(new Experimenter(), "e");

        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }

    @Override
    public void run()
    {
        try
        {
            funk();
        } 
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

我曾预料到,因为一次只有一个线程可以使用同步块,它会打印如下所示的内容,每个内部之间有5秒的间隙:

start
start
start
start
start
in
out
in
out
in
out
in
out
in
out

但相反,我得到以下内容。所有的ins,5秒后,所有的出局。:

start
start
start
start
start
in
in
in
in
in
out
out
out
out
out

有人可以帮忙解释一下吗?

4 个答案:

答案 0 :(得分:3)

非常简单:您的a之间不会共享Experimenter;它是一个实例变量,每Experimenter一个。顺便提一句,volatile在这种情况下几乎没用。

如果您想要共享锁,请将其设为private static final变量。在这种情况下也不需要volatile

但我会选择@ JBNizet的更清洁的解决方案。

编辑:为什么final?因为保证在初始化后永远不会改变;但final变量最重要的一个方面来自Java内存模型,它指出final变量的初始化发生在任何读取此变量之前。这是一个非常强大的规则。

答案 1 :(得分:2)

每个线程在自己的锁上同步,因为每个线程都使用自己的Experimenter实例,每个Experimenter实例都有自己的Integer实例作为锁。如果希望线程同步,则需要在所有实例之间共享唯一锁。你不应该使用整数,BTW。使用简单的对象:

final Object sharedLock = new Object();
Thread a = new Thread(new Experimenter(sharedLock), "a");
Thread b = new Thread(new Experimenter(sharedLock), "b");
...

使用final关键字: 它不是严格需要的。但锁定,特别是当它们被声明为字段时,应该是最终的,以确保您不会意外地为变量分配新值,这将导致线程使用两个不同的锁定对象

答案 2 :(得分:1)

这肯定会对你有用

public void funk() throws InterruptedException
{
    synchronized (Experimenter.class)
    {
        System.out.println("in");
        Thread.sleep(5000);
        System.out.println("out");
    }
}

答案 3 :(得分:1)

将字段a声明为static,它将按预期工作。在您的代码中,该字段是实例成员,因此您实际上有五个不同的监视器。

相关问题