Java编译器能否对同步语句重新排序以进行优化?
同步语句是否可以重新排序。即: 可以: 成为:Java编译器能否对同步语句重新排序以进行优化?,java,multithreading,Java,Multithreading,同步语句是否可以重新排序。即: 可以: 成为: synchronized(B) { synchronized(A) { ...... } } 是和否 顺序必须一致 假设您正在两个银行帐户之间创建一个事务,并且总是先抓取发送方的锁,然后再抓取接收方的锁。问题是——假设丹和鲍勃想同时向对方转账 线程1可能会抓住Dan的锁,因为它处理Dan对Bob的事务。 然后线程2抓取Bob的锁,处理Bob对Dan的事务 然后,砰,死锁 道德是: 少锁 阅读。我的例子就是从那
synchronized(B) {
synchronized(A) {
......
}
}
是和否
顺序必须一致
假设您正在两个银行帐户之间创建一个事务,并且总是先抓取发送方的锁,然后再抓取接收方的锁。问题是——假设丹和鲍勃想同时向对方转账
线程1可能会抓住Dan的锁,因为它处理Dan对Bob的事务。然后线程2抓取Bob的锁,处理Bob对Dan的事务 然后,砰,死锁 道德是:
然后线程2抓取Bob的锁,处理Bob对Dan的事务 然后,砰,死锁 道德是:
JVM不会以与您编程时不同的顺序获取锁。我怎么知道的?因为否则就不可能解决我答案前半部分的问题。编译器永远不会对同步语句重新排序,因为它对最终发生的事情有很大影响 同步块用于获得放置在同步括号之间的特定对象的锁
private final Object LOCK_1 = new Object();
public void foo(){
synchronized(LOCK_1){
//code here...
}
}
获取对象锁_1的锁,并在同步块完成时释放它。由于同步块用于防止并发访问,因此有时可能需要使用多个锁,尤其是在写入/读取多个线程不安全的对象时
考虑以下使用嵌套同步块的代码:
private final Object LOCK_1 = new Object();
private final Object LOCK_2 = new Object();
public void bar(){
synchronized(LOCK_1){
//Point A
synchronized(LOCK_2){
//Point B
}
//Point C
}
//Point D
}
如果我们看一看A、B、C、D点,我们就能明白为什么同步顺序很重要。首先在点A处,获得锁_1的锁,因此尝试获得锁_1的任何其他线程都被放入队列中。
在点B,当前执行的线程同时拥有锁1和锁2的锁。
在点C处,当前执行的线程已释放了锁2的锁
在点D,当前执行的线程已释放所有锁。
如果我们翻转这个例子,决定在外部块上放置LOCK_2,您会意识到线程获取锁的顺序会发生变化,这对它最终的操作有很大影响。通常,当我使用同步块制作程序时,我在访问的每个线程不安全资源中使用一个互斥对象(或每个组使用一个互斥对象)。假设我想使用LOCK_1读取流,并使用LOCK_2写入流。认为交换锁定顺序意味着相同的事情是不合逻辑的 假设锁2(写锁)被另一个线程持有。如果我们在外部块上有LOCK_1,那么当前执行的线程至少可以处理所有读取的代码,然后再放入写入锁的队列(本质上是在点a执行代码的能力)。如果我们翻转锁的顺序,当前执行的线程将不得不等待写入完成,然后继续读取和写入,同时按住写入锁(也一直读取) 当锁的顺序被切换时会出现另一个问题(不一致的是,一些代码先有锁1,而另一些代码先有锁2)。考虑两个线程都急切地尝试执行具有不同锁定顺序的代码。螺纹1从外缸体中获得锁紧装置1,螺纹2从外缸体中获得锁紧装置2。现在,当线程1尝试获取锁2时,它无法获得,因为线程2拥有它。当线程2试图获得锁1时,它也不能,因为线程1拥有它。这两个线程基本上永远互相阻塞,形成了一种死锁状态
为了回答您的问题,如果您想立即锁定两个对象,而不在锁之间进行任何处理,那么顺序是不相关的(基本上在A点或C点没有处理)但是必须在整个程序中保持顺序一致,以避免死锁。编译器永远不会对同步语句重新排序,因为它对最终发生的事情有很大影响 同步块用于获得放置在同步括号之间的特定对象的锁
private final Object LOCK_1 = new Object();
public void foo(){
synchronized(LOCK_1){
//code here...
}
}
获取对象锁_1的锁,并在同步块完成时释放它。因为同步块是用来防止并发访问的