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

时间:2014-01-03 15:04:59

标签: 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;
  }

}

同步块:

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;
}

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

10 个答案:

答案 0 :(得分:67)

这不是更好,只是不同。

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

public synchronized int getCount() {
    // ...
}

这就像你写的那样。

public int getCount() {
    synchronized (this) {
        // ...
    }
}

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

答案 1 :(得分:34)

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

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

如果使用私有数据成员的锁定,则可以阻止此操作,因为恶意用户无法获取私有对象的锁定。

private final Object lockObject = new Object();

public void getCount() {
    synchronized( lockObject ) {
        ...
    }
}

Bloch的Effective Java(第2版),第70项

中提到了这种技术

答案 2 :(得分:21)

区别在于获取锁定:

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

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

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

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

这同样适用于静态方法:同步静态方法将获取整个类对象中的锁定,而静态方法中的同步块将获取括号内对象的锁定。

答案 3 :(得分:6)

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

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

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

答案 4 :(得分:5)

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

  1. 在不同的对象上进行同步
  2. 限制同步范围
  3. 现在您的具体示例是可疑的double-checked locking模式的示例(在较旧的Java版本中,它被破坏,并且很容易做错)。

    如果您的初始化很便宜,最好立即使用final字段进行初始化,而不是第一次请求,这也可以消除同步的需要。

答案 5 :(得分:2)

在你的情况下两者都是等价的!

同步静态方法相当于相应Class对象上的synchronized块。

实际上,当您在与Class对象相对应的监视器上声明同步静态方法锁定时。

public static synchronized int getCount() {
    // ...
}

相同
public int getCount() {
    synchronized (ClassName.class) {
        // ...
    }
}

答案 6 :(得分:2)

仅当您希望您的类是线程安全时,才应使用

同步。事实上,大多数类都不应该使用同步。 同步方法只会在此对象上提供 lock ,并且仅在执行期间提供。如果你真的想让你的课程线程安全,你应该考虑让你的变量不稳定同步访问。

使用同步方法的一个问题是,该类的所有成员都会使用相同的 lock ,这会使您的程序变慢。在你的情况下,synchronized方法和块将执行没有不同。我建议使用专用的并使用 synchronized块这样的内容。

public class AClass {
private int x;
private final Object lock = new Object();     //it must be final!

 public void setX() {
    synchronized(lock) {
        x++;
    }
 }
}

答案 7 :(得分:1)

因为锁是昂贵的,所以当你使用同步块时,只有在_instance == null时锁定,并且在_instance最终初始化之后你永远不会锁定。但是,当您在方法上进行同步时,即使在_instance初始化之后,您也会无条件地锁定。这是双重检查锁定优化模式http://en.wikipedia.org/wiki/Double-checked_locking背后的想法。

答案 8 :(得分:1)

它不应被视为最佳使用问题,但它实际上取决于用例或场景。

同步方法

整个方法可以标记为synchronized,从而导致对此引用(实例方法)或类(静态方法)的隐式锁定。这是实现同步的非常方便的机制。

<强>步骤 线程访问synchronized方法。它隐式获取锁并执行代码。 如果其他线程想要访问上述方法,则必须等待。线程无法获得锁定,将被阻止并且必须等到锁定被释放。

同步阻止

要获取特定代码块对象的锁定,同步块最适合。由于块足够,使用同步方法将是一种浪费。

更具体地说,使用同步块,可以定义想要获取锁定的对象引用。

答案 9 :(得分:0)

同步块与同步方法之间的经典区别是同步方法锁定整个对象。同步块只是将代码锁定在该块内。

同步方法:基本上这两个同步方法禁用多线程。因此,一个线程完成method1(),而另一个线程等待Thread1完成。

SyncExerciseWithSyncMethod类{

public synchronized void method1() {
    try {
        System.out.println("In Method 1");
        Thread.sleep(5000);
    } catch (Exception e) {
        System.out.println("Catch of method 1");
    } finally {
        System.out.println("Finally of method 1");
    }

}

public synchronized void method2() {
    try {
        for (int i = 1; i < 10; i++) {
            System.out.println("Method 2 " + i);
            Thread.sleep(1000);
        }
    } catch (Exception e) {
        System.out.println("Catch of method 2");
    } finally {
        System.out.println("Finally of method 2");
    }
}

}

输出

在方法1中

最后是方法1

方法2 1

方法2 2

方法2 3

方法2 4

方法2 5

方法2 6

方法2 7

方法2 8

方法2 9

最后是方法2


同步块:允许多个线程同时访问同一对象[启用多线程]。

SyncExerciseWithSyncBlock类{

public Object lock1 = new Object();
public Object lock2 = new Object();

public void method1() {
    synchronized (lock1) {
        try {
            System.out.println("In Method 1");
            Thread.sleep(5000);
        } catch (Exception e) {
            System.out.println("Catch of method 1");
        } finally {
            System.out.println("Finally of method 1");
        }
    }

}

public void method2() {

    synchronized (lock2) {
        try {
            for (int i = 1; i < 10; i++) {
                System.out.println("Method 2 " + i);
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            System.out.println("Catch of method 2");
        } finally {
            System.out.println("Finally of method 2");
        }
    }
}

}

输出

在方法1中

方法2 1

方法2 2

方法2 3

方法2 4

方法2 5

最后是方法1

方法2 6

方法2 7

方法2 8

方法2 9

最后是方法2