【问题标题】:Synchronising twice on the same object?在同一个对象上同步两次?
【发布时间】:2010-09-19 23:25:30
【问题描述】:

我想知道在 Java 中如果我在同一个对象上同步两次是否会出现任何奇怪的行为?

场景如下

pulbic class SillyClassName {

    object moo;
    ...
    public void method1(){
        synchronized(moo)
        {
            ....
            method2();
            ....
        }
    }

    public void method2(){
        synchronized(moo)
        {
            doStuff();
        }
    }
}

两种方法都使用对象并在其上同步。第二个方法被第一个方法调用时会因为被锁定而停止吗?

我不这么认为,因为它是同一个线程,但我不确定可能会出现任何其他奇怪的结果。

【问题讨论】:

  • 想知道如果不从方法 1 调用方法 2 会发生什么,如果方法 1 和 2 同时被对象 1 和 2 调用。由于这两个方法都锁定在同一个对象 moo 上,一次只会执行这两种方法中的一种?

标签: java multithreading synchronization mutex


【解决方案1】:

在 java 中,方法上的 synchronized 关键字基本上会在当前对象上同步,因此实际上它正在隐式执行您上面建议的操作。

在一种方法中同步一个对象,然后在另一种方法中对同一个对象进行同步,您不会遇到问题,因为正如您所说,当前线程已经持有该对象的锁。

【讨论】:

    【解决方案2】:

    没问题。在您的示例中,(一旦您修复代码以消除您将获得的编译警告;)),同步确保方法 1 和方法 2 中的块不会同时执行。

    这就是同步的重点。 :)


    编辑:抱歉,错过了您的部分问题,但 Phill 回答了。总而言之,单个线程不能自己死锁。

    【讨论】:

      【解决方案3】:

      可重入

      同步块使用可重入锁,这意味着如果线程已经持有锁,它可以重新获取它而不会出现问题。因此,您的代码将按预期工作。

      查看Java Tutorial页面底部Intrinsic Locks and Synchronization

      自 2015 年 1 月起引用……

      重入同步

      回想一下,一个线程无法获得另一个线程拥有的锁。但是线程可以获取它已经拥有的锁。允许一个线程多次获取同一个锁会启用重入同步。这描述了一种情况,同步代码直接或间接调用一个也包含同步代码的方法,并且两组代码都使用相同的锁。如果没有可重入同步,同步代码将不得不采取许多额外的预防措施来避免线程导致自身阻塞。

      【讨论】:

        【解决方案4】:

        不,如果第一个方法调用第二个方法将不会停止。不会出现奇怪的结果(除了检查锁的轻微开销。这无关紧要。从 Java 6 开始,您在 JVM 中有锁粗化 - Java SE 6 Performance White Paper。)

        例如,看一下 java.util.Vector 的源代码。在同步方法中有很多对其他同步方法的调用。

        【讨论】:

          【解决方案5】:

          Java 似乎完全支持同一线程对一个对象的嵌套锁。这意味着如果一个线程对一个对象有一个外部锁和一个内部锁,而另一个线程试图锁定同一个对象,则第二个线程将被挂起,直到第一个线程释放 两个锁线程。

          我的测试是在 Java 6 SE 下完成的。

          【讨论】:

          • 在嵌套的同步块中,同一个线程只会获取一个锁。
          【解决方案6】:

          我认为我们必须为您尝试做的事情使用可重入锁。这是来自http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/ReentrantLock.html 的sn-p。

          我们所说的可重入锁是什么意思?简单来说就是有一个与锁相关的获取计数,如果持有锁的线程再次获取它,获取计数就会增加,然后需要释放两次锁才能真正释放锁。这与同步的语义相似;如果一个线程进入一个由该线程已经拥有的监视器保护的同步块,该线程将被允许继续,当线程退出第二个(或后续)同步块时,锁不会被释放,而只会被释放当它退出它进入的受该监视器保护的第一个同步块时。

          虽然我没有尝试过,但我想如果你想做上面的事情,你必须使用重入锁。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2022-01-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多