【问题标题】:Java synchronized block unexpected behaviorJava 同步块意外行为
【发布时间】:2014-12-30 13:00:23
【问题描述】:

我有以下代码:

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

但是,相反,我得到以下信息。所有的输入,5 秒后,所有的输出。:

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

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

【问题讨论】:

    标签: java multithreading synchronized


    【解决方案1】:

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

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

    但我会选择@JBNizet 的解决方案,它更干净。

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

    【讨论】:

    • 你能解释一下'final'的必要性吗?
    • 那么他必须使用类级锁??
    • @Prashant 不,基本上他不需要;但无论使用哪个锁,它都必须在所有线程之间共享。 private static final 变量是 一个 解决方案,还有其他的,例如 @JBNizet 的(我认为它更干净)
    【解决方案2】:

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

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

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

    【讨论】:

    • 你能解释一下'final'的必要性吗?
    • 这不是绝对需要的。但是锁,特别是当它们被声明为字段时,应该是 final 的,以确保您不会意外地为变量分配一个新值,这会导致线程使用两个不同的锁对象。
    • 您能否将此添加到您的答案中以更好地帮助未来的用户?
    【解决方案3】:

    这肯定对你有用

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

    【讨论】:

    • 如果您使用的是类级别锁,那么变量应该是静态的,就像其他答案一样!!!
    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-01
      • 1970-01-01
      • 2011-12-01
      • 1970-01-01
      • 2018-07-08
      • 2011-10-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多