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

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


StringBuffer aStringBufferObject = new StringBuffer("A");

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

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

例如,在第二个示例中,由于锁与“this”实例无关,线程仍将能够执行同步块(synchronized block)内的代码吗?

我知道同步一个方法或代码块会阻止多个线程同时访问该块/方法,但是指定要锁定的对象的目的是什么,以及在对象中指定对象的方式有何不同上面的例子?

最佳答案



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

  • 您想要不使用this同步对基元的访问。您只能在Object上进行同步,因为每个Object与Java中的隐式监视器关联。基元没有此类隐式监视器,因此您需要使用锁对象。使用包装器类是一个糟糕而错误的选择,尤其是当您最终使用modifying the lock object in the guarded block时。
  • 您想在实际上保护关键部分的对象上进行同步,而在this上进行同步将无法保证线程安全。例如,如果要同步对在ArrayList类的实例之间共享的A实例的访问,则在A实例上进行同步是没有用的。一个线程可能会创建一个A的新实例并获得对列表的访问权限,而另一个线程正在对其进行修改。如果使用所有线程都必须争用的其他锁,则可以保护列表;否则,您可以保护列表。此锁可以是与A.class关联的锁,但也可以是将提供相同保证的任何对象。
  • 您想要执行锁拆分,以确保不同的 protected 块受不同的锁(而不是相同的锁)保护。换句话说,如果允许不同的线程获取不同的锁来访问不同的关键部分是线程安全的,则每个关键部分都可以具有不同的锁。

  • 以下是拆分锁用法的示例:
    private Object method1Lock = new Object();
    private Object method2Lock = new Object();
    
    public void method1(){
        synchronized(method1Lock){
            ....
        }
    }
    
    public void method2(){
        synchronized(method2Lock){
            ....
        }
    }
    

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

    关于你的另一个问题,



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

    09-25 23:35
    查看更多