8

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

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

StringBuffer aStringBufferObject = new StringBuffer("A");

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

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

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

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

4

3 回答 3

8

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

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

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

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

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

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

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

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


关于你的另一个问题,

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

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

于 2011-07-24T14:41:57.430 回答
2

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

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

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

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

于 2011-07-24T14:24:23.520 回答
2

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

于 2011-07-24T14:40:33.707 回答