避免在Java中同步(this)?

每当有关于Java同步的SO问题出现时,有些人就非常希望指出应该避免synchronized(this) 。 相反,他们声称,锁定私人参考是首选。

一些给定的原因是:

  • 一些邪恶的代码可能会偷你的锁(非常流行这一个,也有一个“意外”的变体)
  • 同一类中的所有同步方法使用完全相同的锁,这会降低吞吐量
  • 你(不必要地)暴露太多的信息
  • 包括我在内的其他人认为synchronized(this)是一个习惯用法(在Java库中也是如此),它是安全的并且很好理解。 它不应该被避免,因为你有一个错误,你不知道你的多线程程序正在发生什么。 换句话说:如果它适用,然后使用它。

    我有兴趣看到一些真实世界的例子(没有foobar的东西),在synchronized(this)也可以完成这项工作的时候避免锁定this是可取的。

    因此: 你应该总是避免synchronized(this)并将其替换为私人引用上的锁?


    更多信息(更新为答案):

  • 我们正在谈论实例同步
  • 考虑隐式( synchronized方法)和显式形式的synchronized(this)
  • 如果您引用Bloch或其他权威人士的话,请不要忽略您不喜欢的部分(例如,Effective Java,线程安全上的项目:通常它是实例本身的锁,但也有例外)。
  • 如果你的锁定需要粒度而不是synchronized(this)提供,那么synchronized(this)不适用,所以这不是问题

  • 我会分别介绍每一点。

  • 一些邪恶的代码可能会偷你的锁(非常受欢迎,也有一个“意外”的变体)

    我更偶然地担心。 什么它相当于是这个用途this是你的类暴露接口的一部分,并应记录在案。 有时需要其他代码使用您的锁的能力。 像Collections.synchronizedMap (请参阅javadoc)这是事实。

  • 同一类中的所有同步方法使用完全相同的锁,这会降低吞吐量

    这是过于简单化的想法; 刚刚摆脱synchronized(this)不会解决问题。 适当的吞吐量同步将需要更多思考。

  • 你(不必要地)暴露太多的信息

    这是#1的变体。 synchronized(this)是您的界面的一部分。 如果你不想/需要这个暴露,不要这样做。


  • 那么,首先应该指出的是:

    public void blah() {
      synchronized (this) {
        // do stuff
      }
    }
    

    在语义上等同于:

    public synchronized void blah() {
      // do stuff
    }
    

    这是不使用synchronized(this)一个原因。 你可能会争辩说,你可以在synchronized(this)块周围做些事情。 通常的原因是尽量避免必须进行同步检查,这会导致各种并发问题,特别是双重检查锁定问题,这些问题仅表明进行相对简单的检查是多么困难线程安全的。

    私人锁是一种防御机制,这绝不是一个坏主意。

    另外,正如你所提到的,私有锁可以控制粒度。 一个对象上的一组操作可能与另一个对象完全无关,但synchronized(this)将相互排除对所有对象的访问。

    synchronized(this)只是真的不给你任何东西。


    当您使用synchronized(this)时,您正在使用类实例作为锁本身。 这意味着虽然线程1获取锁定,但线程2应该等待

    假设下面的代码

    public void method1() {
        do something ...
        synchronized(this) {
            a ++;      
        }
        ................
    }
    
    
    public void method2() {
        do something ...
        synchronized(this) {
            b ++;      
        }
        ................
    }
    

    方法1修改变量a和方法2修改变量b,应该避免两个线程同时修改同一个变量,并且它是。 但是,当thread1修改a和thread2修改b时,它可以在没有任何竞态条件下执行。

    不幸的是,上面的代码不会允许这样做,因为我们使用相同的引用来锁定; 这意味着即使它们不处于竞争状态,线程也应该等待,显然这些代码会牺牲程序的并发性。

    解决方案是对两个不同的变量使用2个不同的锁。

      class Test {
            private Object lockA = new Object();
            private Object lockB = new Object();
    
    public void method1() {
        do something ...
        synchronized(lockA) {
            a ++;      
        }
        ................
    }
    
    
    public void method2() {
        do something ...
        synchronized(lockB) {
            b ++;      
        }
        ................
     }
    

    上面的例子使用了更细粒度的锁(2个锁而不是1个)(分别为变量a和b分别为lockA和lockB),因此可以实现更好的并发性,另一方面,它比第一个例子变得更加复杂。

    链接地址: http://www.djcxy.com/p/2131.html

    上一篇: Avoid synchronized(this) in Java?

    下一篇: IllegalArgumentException or NullPointerException for a null parameter?