有人能告诉我同步方法比同步块的优势与一个例子吗?
当前回答
当java编译器将源代码转换为字节码时,它处理同步方法和同步块的方式非常不同。
当JVM执行一个同步方法时,执行线程识别出该方法的method_info结构体设置了ACC_SYNCHRONIZED标志,然后它自动获取对象的锁,调用该方法,并释放锁。如果发生异常,线程自动释放锁。
另一方面,同步方法块绕过了JVM对获取对象锁和异常处理的内置支持,并要求显式地用字节代码编写功能。如果读取带有同步块的方法的字节代码,您将看到十多个额外的操作来管理此功能。
这显示了生成同步方法和同步块的调用:
public class SynchronizationExample {
private int i;
public synchronized int synchronizedMethodGet() {
return i;
}
public int synchronizedBlockGet() {
synchronized( this ) {
return i;
}
}
}
synchronizedMethodGet()方法生成以下字节代码:
0: aload_0
1: getfield
2: nop
3: iconst_m1
4: ireturn
下面是synchronizedBlockGet()方法的字节代码:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_0
5: getfield
6: nop
7: iconst_m1
8: aload_1
9: monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow
One significant difference between synchronized method and block is that, Synchronized block generally reduce scope of lock. As scope of lock is inversely proportional to performance, its always better to lock only critical section of code. One of the best example of using synchronized block is double checked locking in Singleton pattern where instead of locking whole getInstance() method we only lock critical section of code which is used to create Singleton instance. This improves performance drastically because locking is only required one or two times.
在使用同步方法时,如果混合使用静态同步方法和非静态同步方法,则需要格外小心。
其他回答
同步的方法
优点:
您的IDE可以指示同步方法。 语法更加紧凑。 强制将同步块分割为单独的方法。
缺点:
与此同步,因此外部人员也可以与之同步。 将代码移到同步块之外更加困难。
同步块
优点:
允许为锁使用私有变量,从而将锁强制留在类内部。 同步块可以通过搜索变量的引用来找到。
缺点:
语法更复杂,因此使代码更难阅读。
就我个人而言,我更喜欢使用同步方法,类只关注需要同步的东西。这样的类应该尽可能小,所以应该很容易检查同步。其他人不需要关心同步。
通常在方法级别上使用锁是不礼貌的。为什么要通过锁定整个方法来锁定一段不能访问任何共享资源的代码呢?因为每个对象都有一个锁,所以可以创建虚拟对象来实现块级同步。 块级的效率更高,因为它不锁定整个方法。
这里有一些例子
方法级
class MethodLevel {
//shared among threads
SharedResource x, y ;
public void synchronized method1() {
//multiple threads can't access
}
public void synchronized method2() {
//multiple threads can't access
}
public void method3() {
//not synchronized
//multiple threads can access
}
}
块级别
class BlockLevel {
//shared among threads
SharedResource x, y ;
//dummy objects for locking
Object xLock = new Object();
Object yLock = new Object();
public void method1() {
synchronized(xLock){
//access x here. thread safe
}
//do something here but don't use SharedResource x, y
// because will not be thread-safe
synchronized(xLock) {
synchronized(yLock) {
//access x,y here. thread safe
}
}
//do something here but don't use SharedResource x, y
//because will not be thread-safe
}//end of method1
}
(编辑)
对于像Vector和Hashtable这样的集合,当ArrayList或HashMap不同步时,它们是同步的,你需要设置synchronized关键字或调用Collections synchronized方法:
Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list
使用同步块,您可以有多个同步器,因此多个同时但不冲突的事情可以同时进行。
同步的方法
同步方法有两个效果。 首先,当一个线程正在为一个对象执行同步方法时,所有为同一对象调用同步方法的其他线程将阻塞(暂停执行),直到第一个线程处理完该对象。
其次,当同步方法退出时,它自动与同一对象的同步方法的任何后续调用建立happens-before关系。这保证了对对象状态的更改对于所有线程都是可见的。
注意,构造函数不能同步——在构造函数中使用synchronized关键字是一个语法错误。同步构造函数没有意义,因为只有创建对象的线程才能在构造对象时访问它。
同步语句
与同步方法不同,同步语句必须指定提供内在锁的对象:大多数情况下,我使用它来同步对列表或映射的访问,但我不想阻塞对对象的所有方法的访问。
Q:内在锁和同步 同步是围绕一个称为内在锁或监视器锁的内部实体构建的。(API规范通常将此实体简单地称为“监视器”)内在锁在同步的两个方面都发挥作用:强制独占访问对象的状态,并建立对可见性至关重要的happens-before关系。
Every object has an intrinsic lock associated with it. By convention, a thread that needs exclusive and consistent access to an object's fields has to acquire the object's intrinsic lock before accessing them, and then release the intrinsic lock when it's done with them. A thread is said to own the intrinsic lock between the time it has acquired the lock and released the lock. As long as a thread owns an intrinsic lock, no other thread can acquire the same lock. The other thread will block when it attempts to acquire the lock.
package test;
public class SynchTest implements Runnable {
private int c = 0;
public static void main(String[] args) {
new SynchTest().test();
}
public void test() {
// Create the object with the run() method
Runnable runnable = new SynchTest();
Runnable runnable2 = new SynchTest();
// Create the thread supplying it with the runnable object
Thread thread = new Thread(runnable,"thread-1");
Thread thread2 = new Thread(runnable,"thread-2");
// Here the key point is passing same object, if you pass runnable2 for thread2,
// then its not applicable for synchronization test and that wont give expected
// output Synchronization method means "it is not possible for two invocations
// of synchronized methods on the same object to interleave"
// Start the thread
thread.start();
thread2.start();
}
public synchronized void increment() {
System.out.println("Begin thread " + Thread.currentThread().getName());
System.out.println(this.hashCode() + "Value of C = " + c);
// If we uncomment this for synchronized block, then the result would be different
// synchronized(this) {
for (int i = 0; i < 9999999; i++) {
c += i;
}
// }
System.out.println("End thread " + Thread.currentThread().getName());
}
// public synchronized void decrement() {
// System.out.println("Decrement " + Thread.currentThread().getName());
// }
public int value() {
return c;
}
@Override
public void run() {
this.increment();
}
}
用同步方法,块和不同步交叉检查不同的输出。
我知道这是一个老问题,但通过快速阅读这里的回答,我并没有看到任何人提到同步方法有时可能是错误的锁。 摘自Java并发实践(第72页):
public class ListHelper<E> {
public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...
public syncrhonized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
上面的代码看起来是线程安全的。然而,现实并非如此。在这种情况下,锁在类的实例上获得。但是,该列表可能被另一个不使用该方法的线程修改。正确的方法是使用
public boolean putIfAbsent(E x) {
synchronized(list) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
}
上面的代码将阻止所有试图修改list的线程修改列表,直到同步块完成。
推荐文章
- 如何使一个Java通用方法静态?
- for-each循环和迭代器,哪个更有效?
- 泛型类中的静态方法?
- 如何在JPA中持久化类型列表<字符串>的属性?
- 考虑在配置中定义一个'package'类型的bean [Spring-Boot]
- Java注释中的/**和/*
- java8 LocalDate Jackson格式
- Android Studio谷歌JAR文件导致GC开销限制超过错误
- 如何在Intellij生成串行版本UID
- “比较法违反其总合同!”
- 从Java项目生成UML类图
- 正确地从一个<Integer>的列表中移除一个整数
- Java开关语句:需要常量表达式,但它是常量
- Java的assertEquals方法可靠吗?
- 如何在Java中获得系统变量值?