[英]Why is synchronized block better than synchronized method?
我已经开始学习线程同步。
同步方法:
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
块比synchronized
方法更好?
这不是更好的问题,只是不同。
当您同步一个方法时,您实际上是在同步到对象本身。 在静态方法的情况下,您正在同步到对象的类。 所以下面两段代码的执行方式是一样的:
public synchronized int getCount() {
// ...
}
这就像你写的一样。
public int getCount() {
synchronized (this) {
// ...
}
}
如果要控制与特定对象的同步,或者只想将方法的一部分同步到该对象,则指定一个synchronized
块。 如果在方法声明中使用了synchronized
关键字,它会将整个方法同步到对象或类。
虽然通常不是一个问题,但从安全角度来看,最好在私有对象上使用同步,而不是将它放在方法上。
将它放在方法上意味着您正在使用对象本身的锁来提供线程安全。 通过这种机制,您代码的恶意用户也有可能获得您对象的锁,并永久持有它,从而有效地阻塞其他线程。 非恶意用户可以在不经意间有效地做同样的事情。
如果您使用私有数据成员的锁,则可以防止这种情况发生,因为恶意用户不可能获得您私有对象的锁。
private final Object lockObject = new Object();
public void getCount() {
synchronized( lockObject ) {
...
}
}
Bloch 的 Effective Java(第 2 版)第 70 项中提到了这种技术
不同之处在于获取的是哪个锁:
同步方法获取整个对象的锁。 这意味着当一个线程正在运行该方法时,没有其他线程可以在整个对象中使用任何同步方法。
synchronized 块在synchronized 关键字之后的括号之间获取对象中的锁。 这意味着在同步块退出之前,没有其他线程可以获取锁定对象的锁定。
因此,如果您想锁定整个对象,请使用同步方法。 如果您想让其他线程可以访问对象的其他部分,请使用同步块。
如果仔细选择锁定的对象,同步块将导致较少的争用,因为整个对象/类都没有被阻塞。
这同样适用于静态方法:同步静态方法将获取整个类对象中的锁,而静态方法内的同步块将获取括号之间对象中的锁。
同步块和同步方法的区别如下:
同步块: synchronized(this){}
同步方法: public synchronized void fun(){}
定义“更好”。 同步块只是更好,因为它允许您:
现在,您的具体示例是可疑的双重检查锁定模式的示例(在较旧的 Java 版本中,它已损坏,并且很容易出错)。
如果您的初始化成本较低,最好立即使用 final 字段进行初始化,而不是在第一次请求时进行初始化,这也将消除同步的需要。
仅当您希望您的类是线程安全的时才应使用同步。 事实上,大多数类无论如何都不应该使用同步。 同步方法只会在此对象上提供锁定,并且仅在其执行期间提供锁定。 如果你真的想让你的类线程安全,你应该考虑让你的变量可变或同步访问。
使用同步方法的问题之一是该类的所有成员都将使用相同的锁,这会使您的程序变慢。 在您的情况下,同步方法和块将执行没有什么不同。 我会推荐的是使用专用锁并使用类似这样的同步块。
public class AClass {
private int x;
private final Object lock = new Object(); //it must be final!
public void setX() {
synchronized(lock) {
x++;
}
}
}
在你的情况下,两者都是等价的!
同步一个静态方法相当于在相应的 Class 对象上同步一个块。
实际上当你声明一个synchronized静态方法时,是在Class对象对应的monitor上获得锁的。
public static synchronized int getCount() {
// ...
}
与
public int getCount() {
synchronized (ClassName.class) {
// ...
}
}
因为锁是昂贵的,当你使用同步块时,你只在_instance == null
锁定,并且在_instance
最终初始化之后你将永远不会锁定。 但是当您同步方法时,您会无条件锁定,即使在_instance
初始化之后也是如此。 这是双重检查锁定优化模式http://en.wikipedia.org/wiki/Double-checked_locking背后的想法。
不应将其视为最佳使用问题,但它确实取决于用例或场景。
同步方法
可以将整个方法标记为同步,从而导致对 this 引用(实例方法)或类(静态方法)的隐式锁定。 这是实现同步的非常方便的机制。
步骤线程访问同步方法。 它隐式地获取锁并执行代码。 如果其他线程要访问上述方法,则必须等待。 线程无法获得锁,将被阻塞,必须等到锁被释放。
同步块
要为一组特定的代码块获取对象上的锁,同步块是最合适的。 由于一个块就足够了,使用同步方法将是一种浪费。
更具体地说,使用 Synchronized Block ,可以定义要获取锁的对象引用。
Synchronized 块和 Synchronized 方法之间的一个经典区别是 Synchronized 方法锁定整个对象。 同步块只是锁定块内的代码。
同步方法:基本上这两种同步方法禁用多线程。 所以一个线程完成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 中
方法一的最后
方法 2 1
方法 2 2
方法 2 3
方法 2 4
方法 2 5
方法 2 6
方法 2 7
方法 2 8
方法 2 9
方法二的最后
同步块:允许多个线程同时访问同一个对象【启用多线程】。
类 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
方法一的最后
方法 2 6
方法 2 7
方法 2 8
方法 2 9
方法二的最后
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.