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的事务

然后,砰,死锁

道德是:

  • 少锁
  • 阅读。我的例子就是从那里得到的。我和其他人一样喜欢争论编程方面书籍的优点,但是很少有人能在两个封面之间全面地报道一个困难的话题,所以请欣赏
  • 所以这是答案的一部分,我猜你可能一直想问的其他事情,因为我坚定地期望我表现得通灵

    JVM不会以与您编程时不同的顺序获取锁。我怎么知道的?因为否则我就不可能解决我答案前半部分的问题。

    是和否

    顺序必须一致

    假设您正在两个银行帐户之间创建一个事务,并且总是先抓取发送方的锁,然后再抓取接收方的锁。问题是——假设丹和鲍勃想同时向对方转账

    线程1可能会抓住Dan的锁,因为它处理Dan对Bob的事务。
    然后线程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的锁,并在同步块完成时释放它。因为同步块是用来防止并发访问的