【问题标题】:No race condition: Two blocks with different locks but same shared data无竞争条件:两个块具有不同的锁但共享数据相同
【发布时间】:2018-10-09 08:07:49
【问题描述】:

我有两个线程 thread_1thread_2 在同一个对象 unsafeObj 上调用不同的方法。

  • thread_1 调用unsafeObj.incrementVAR_v1() 10 次
  • thread_2 调用unsafeObj.incrementVAR_v2() 10 次

这两个实例方法都有一个 synchronized-block 和不同的锁(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

【问题讨论】:

  • 另外我希望unsafeObj.incrementVAR_v1() 会在新线程开始之前完成,尝试引入大量的计数一些睡眠时间
  • 你能尝试在这两种方法中使用LOCK_1,看看它是如何工作的吗?可能我们误解了同步块的功能,如果您使用不同的锁,它们会同步执行。
  • 我有时会得到正确的结果,每种方法运行 10000 次。所以不要指望它每次都能工作。或者期望它会产生“错误”的结果。两者都可能发生,但数量如此之少,可能不会。
  • @NullReference 是的,当您写入 System.out 时,它也会获得对 OutputStream 实例的锁定
  • 我猜这是因为++VAR 只编译成两行汇编,除非处理器超级忙,否则不太可能分开?

标签: java multithreading race-condition synchronized


【解决方案1】:

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

  • ☝ 比赛条件很难重现
  • ? 尝试复制时使用大量迭代

【讨论】:

    【解决方案2】:

    只是为了好玩(当然,不能保证)

    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
    

    【讨论】:

      猜你喜欢
      • 2021-08-28
      • 1970-01-01
      • 2022-01-15
      • 2012-04-29
      • 2012-11-17
      • 1970-01-01
      • 1970-01-01
      • 2012-01-10
      • 2017-09-12
      相关资源
      最近更新 更多