【问题标题】:Synchronized threads and locking同步线程和锁定
【发布时间】:2011-10-12 01:11:12
【问题描述】:

有人可以在对象锁定的上下文中解释这两个示例之间的区别吗:

public void method1(){
    synchronized(this){
        ....
    }
}

还有

StringBuffer aStringBufferObject = new StringBuffer("A");

public void method2(){
    synchronized(aStringBufferObject){
        ....
    }
}

我知道第一个示例将获得this 实例的锁,第二个示例将获得aStringBufferObject 实例的锁。但我真的不明白两者的效果或区别是什么。

例如,在第二个例子中,线程是否仍然能够执行同步块内的代码,因为锁与'this'实例无关?

我知道同步方法或代码块可以防止多个线程同时访问该块/方法,但是指定要锁定的对象的目的是什么以及对象的方式有什么区别像上面的例子那样指定?

【问题讨论】:

  • @Andrew:啊,我虽然你指的是一篇文章,但现在看到它是一个多帖子。
  • @HFOE "..这是一个多帖子。" 确实。我因很久以前在另一个问题上提到这一点而受到惩罚(主要是因为输入它作为答案 - 然后拒绝删除答案),所以现在采取更“微妙”的方法。 ;)
  • @Andrew:请使用悬停而不是 HFOE 将评论指向我,因为@HFOE 不会将评论指向我的收件箱。有关更多信息,请查看:meta: how-do-comment-replies-work

标签: java multithreading scjp


【解决方案1】:

指定要锁定的对象的目的是什么?

通常,在thisClass 实例上同步更容易(对于静态方法)。但是,在某些情况下,您需要在特定对象而不是隐式锁定 (this) 上进行同步。此类情况包括:

  • 您希望在不使用this 的情况下同步对原语的访问。您只能在 Objects 上同步,因为每个 Object 都与 Java 中的隐式监视器相关联。原语没有这样的隐式监视器,因此您需要使用锁定对象。使用包装类是一个糟糕且不正确的选择,尤其是当您最终选择 modifying the lock object in the guarded block 时。
  • 当在this 上同步时,您希望在实际保护临界区的对象上进行同步,并不能保证线程安全。例如,如果您正在同步访问在类A 的实例之间共享的ArrayList 实例,那么在A 的实例上同步是没有用的。一个线程可能会创建一个A 的新实例并获得对列表的访问权,而另一个线程正在修改它。如果您使用所有线程必须争用的不同锁,那么您可以保护列表;此锁可能是与A.class 关联的锁,但也可能是提供相同保证的任何对象。
  • 您希望执行锁拆分以确保不同的受保护块由不同的锁而不是相同的锁保护。换句话说,如果允许不同的线程获取不同的锁来访问不同的临界区是线程安全的,那么您可以为每个临界区拥有不同的锁。

以下是拆分锁的使用示例:

private Object method1Lock = new Object();
private Object method2Lock = new Object();

public void method1(){
    synchronized(method1Lock){
        ....
    }
}

public void method2(){
    synchronized(method2Lock){
        ....
    }
}

当您可以确保method1method2 的并发执行不违反类不变量时,您将使用拆分锁。这样,您可以提高需要访问同一对象但将调用不同方法的线程之间的性能。


关于你的另一个问题,

例如,在第二个例子中,线程是否仍然能够执行同步块内的代码,因为锁与'this'实例无关?

在第二个示例中,任何进入受保护区域的线程都必须获取与aStringBufferObject 关联的锁。如果另一个线程持有该锁,则当前线程将不会继续进行。当您指定this 时,线程必须获取与当前对象关联的锁。在这两种情况下,线程都必须获取锁;这些示例仅在用作锁的对象上有所不同。

【讨论】:

    【解决方案2】:

    synchronized 块是一个监视器,它忽略了锁定和解锁互斥锁的细节。因为Java中的每个对象都有一个内部锁(参考Object类的源代码),当使用synchronized语句时,JVM会帮你同步临界区。您也可以使用java.util.concurrent.locks 包中的 ReentrantLock 自己同步块。

    【讨论】:

      【解决方案3】:

      在一个对象上同步意味着在同一个对象上同步的其他块将不得不等待。例如:

      public void methodA() {
         synchronized(obj) {
            //Do one job
         }
      }
      
      public void methodB() {
         synchronized(obj) {
            //Do another job
         }
      }
      

      如果您在一个线程中调用methodA(),然后在另一个线程中调用methodB(),则methodB() 不会在methodA() 完成之前完成。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-13
        • 2020-12-07
        • 1970-01-01
        • 2014-05-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多