为什么Synchronized阻止比同步方法更好?

Sab*_*thy 56 java multithreading synchronization

我已经开始在线程中学习同步.

同步方法:

public class Counter {

   private static int count = 0;

   public static synchronized int getCount() {
      return count;
   }

   public synchronized setCount(int count) {
      this.count = count;
   }

}
Run Code Online (Sandbox Code Playgroud)

同步块:

public class Singleton {

   private static volatile Singleton _instance;

   public static Singleton getInstance() {
      if (_instance == null) {
         synchronized(Singleton.class) {
            if (_instance == null)
               _instance = new Singleton();
         }
      }
      return _instance;
   }
}
Run Code Online (Sandbox Code Playgroud)

我什么时候应该使用Synchronized方法和Synchronized块?为什么Synchronized阻止比同步方法更好?

Eri*_*son 72

这不是更好,只是不同的问题.

同步方法时,您实际上正在同步对象本身.对于静态方法,您将同步到对象的类.所以以下两段代码执行相同的方式:

public synchronized int getCount() {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

这跟你写的一样.

public int getCount() {
    synchronized (this) {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

如果要控制与特定对象的同步,或者只希望将某个方法的一部分同步到该对象,则指定一个synchronized块.如果synchronized在方法声明中使用关键字,它将使整个方法与对象或类同步.

  • 是的,它是不同的,但有很好的理由为什么你永远不想同步'this`所以这个答案错过了一点点imo. (6认同)
  • @Voo:我强烈反对你永远不会*想要同步`this`.事实上,当你想连续调用一堆同步方法时,这是一个好处.您可以在对象上同步持续时间,而不必担心为每个方法调用释放和重新获取锁定.有许多不同的模式可用于同步,每种模式都有其自身的优缺点,具体取决于具体情况. (4认同)
  • @Voo类的线程行为不是内部细节.这是您的代码合同的一部分.它经常被省略,但是我是否可以在并发环境中使用类可能是它的一个重要部分.synchronized关键字确实泄漏*你如何管理线程.我不认为这很糟糕,因为您可以更改线程控制并删除关键字而不破坏任何客户端代码. (3认同)
  • 同步方法和唯一的顶级块是“ synchronized(someObj)”的方法之间绝对没有区别,事实上,编译器会生成用于同步的代码,就像“ someObj == this”。因此,这样做没有任何优势,但是您将内部细节暴露给外部世界,这显然破坏了封装。好吧,这有一个好处:您节省了大约20个字节。 (2认同)

wol*_*tle 37

虽然通常不是一个问题,但从安全角度来看,最好在私有对象上使用synchronized,而不是将其放在方法上.

将它放在方法上意味着您正在使用对象本身的锁来提供线程安全性.使用这种机制,您的代码的恶意用户也可能获得对象的锁定,并永久保留它,有效地阻止其他线程.非恶意用户可以无意中有效地做同样的事情.

如果使用私有数据成员的锁定,则可以防止这种情况,因为恶意用户无法获取私有对象的锁定.

private final Object lockObject = new Object();

public void getCount() {
    synchronized( lockObject ) {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

Bloch的Effective Java(第2版),第70项中提到了这种技术

  • @ErickRobertson Image您正在使用公共API提供某种服务.如果您的API公开了一些可变对象,其操作依赖于锁定该对象,则一个恶意客户端可以获取该对象,锁定它,然后永久循环,持有锁.这可能会阻止其他客户端正常运行.它基本上是一种拒绝服务型攻击.所以他们不会搞砸他们自己的应用程序. (10认同)
  • @wolfcastle:你能解释一下"你的代码的恶意用户"是什么吗?搞乱他们自己的应用程序,他们是如何恶意的? (6认同)
  • 甚至不必是恶意的,使用从某个地方获得的对象来锁定是非常容易和无辜的(比如因为你想要同步访问这个对象).最好避免这样的问题 (2认同)
  • 该技术位于《Effective Java》(第 3 版)第 82 项中 (2认同)

Ang*_*ity 28

区别在于获取锁定:

  • synchronized方法获取整个对象的锁定.这意味着当该方法由一个线程运行时,没有其他线程可以在整个对象中使用任何同步方法.

  • synchronized块在synchronized关键字之后获取括号内对象的锁定.意味着在同步块退出之前,没有其他线程可以获取锁定对象的锁定.

因此,如果要锁定整个对象,请使用synchronized方法.如果要将对象的其他部分保持为其他线程可访问,请使用synchronized块.

如果仔细选择锁定对象,则同步块将导致争用较少,因为整个对象/类未被阻止.

这类似于静态方法:同步静态方法将获取整个类对象中的锁,而静态方法中的同步块将在括号内的对象中获取锁.


Mar*_*eel 6

定义'更好'.同步块只是更好,因为它允许您:

  1. 在不同的对象上同步
  2. 限制同步范围

现在,您的具体示例是双重检查锁定模式的示例,这是可疑的(在较旧的Java版本中,它已被破坏,并且很容易做错).

如果您的初始化很便宜,最好立即使用final字段初始化,而不是在第一个请求时,它也可以消除同步的需要.


Ram*_*pta 5

同步块同步方法之间的区别如下:

  1. synchronized块减少了锁定范围,但是 synchronized方法的锁定范围是整个方法.
  2. synchronized块具有更好的性能,因为只有临界区被锁定同步方法的性能低于块.
  3. synchronized块提供对锁定的精细控制,同步方法锁定在由此级别或类级别锁定表示的当前对象上.
  4. synchronized块可以抛出NullPointerException 但是 synchronized方法不抛出.
  5. 同步块: synchronized(this){}

    同步方法: public synchronized void fun(){}