繁体   English   中英

Java同步阻止意外行为

[英]Java synchronized block unexpected behavior

我有以下代码:

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

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

很简单:你的a不会在你的任何Experimenter之间共享; 它是一个实例变量,每个Experimenter一个。 顺便说一句,在这种情况下, volatile几乎无用。

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

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

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

每个线程在其自己的锁上同步,因为每个线程使用自己的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关键字:这不是严格需要的。 但是锁,尤其是当它们声明为字段时,锁应该是最终的,以确保您不会意外地给变量分配新值,这将导致线程使用两个不同的锁对象

这肯定会对你有用

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM