有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java无竞争条件:两个具有不同锁但共享数据相同的块

我有两个线程对同一个对象调用不同的方法

  • 线程1调用unsafeObj.incrementVAR_v1()10次
  • 线程2调用unsafeObj.incrementVAR_v2()10次

这两个实例方法都有一个synchronized块,该块具有不同的锁(LOCK_1和LOCK_2)访问同一实例字段VAR

public void icrementVAR_v1() {
  synchronized(LOCK_1) {
    ++VAR;
    print("Thread 1: "  + VAR)
  }
}

public void incrementVAR_v2() {
  synchronized(LOCK_2) {
    ++VAR;
    print("Thread 2: " + VAR);
  }
}

👉 考虑到这两个synchronized块使用不同的锁,我本以为VAR会被并发访问,从而导致更新丢失(VAR小于20)。然而,这不是我所观察到的。有人能给我解释一下为什么不是这样吗

示例输出:

Thread 2: 2
Thread 1: 1
Thread 2: 3
Thread 1: 4
Thread 2: 5
Thread 1: 6
Thread 2: 7
Thread 1: 8
Thread 2: 9
Thread 1: 10
Thread 2: 11
Thread 1: 12
Thread 2: 13
Thread 1: 14
Thread 2: 15
Thread 1: 16
Thread 2: 17
Thread 1: 18
Thread 2: 19
Thread 1: 20

共 (2) 个答案

  1. # 1 楼答案

    正如我所料,对VAR字段的并发访问(由于有两个不同的锁)确实会导致竞争条件,但为了观察它,需要大量的迭代(在我的情况下,每个线程中有10万次迭代)。经验教训:

    • ☝ 种族条件很难复制
    • 👍 在尝试复制它们时使用大量迭代
  2. # 2 楼答案

    只是为了好玩,在10分钟内完成(当然,这不是保证)

    public class Main {
    
        public static void main(String[] args) {
            Thread t1=new Thread(()->{
                for(int i=0;i<10;i++){
                    icrementVAR_v1();
    
                }
            }
            );
            Thread t2=new Thread(()->{
                for(int i=0;i<10;i++){
                    incrementVAR_v2();
    
                }
            }
            );
            t1.start();
            t2.start();
        }
        static Object LOCK_1=new Object();
        static Object LOCK_2=new Object();
        static int VAR=0;
        public static void icrementVAR_v1() {
            synchronized(LOCK_1) {
                ++VAR;
                Thread.yield();
                System.out.println("Thread 1: "  + VAR);
            }
        }
    
        public static void incrementVAR_v2() {
            synchronized(LOCK_2) {
                ++VAR;
                Thread.yield();
                System.out.println("Thread 2: " + VAR);
            }
        }
        public static void print(String s){
            System.out.println(s);
        }
    
    }
    

    输出示例:

    Thread 1: 2
    Thread 2: 2
    Thread 1: 4
    Thread 2: 5
    Thread 1: 6
    Thread 2: 7
    Thread 1: 8
    Thread 2: 9
    Thread 1: 10
    Thread 1: 11
    Thread 2: 12
    Thread 1: 13
    Thread 2: 14
    Thread 1: 15
    Thread 2: 16
    Thread 1: 17
    Thread 2: 18
    Thread 1: 19
    Thread 2: 19
    Thread 2: 20