为什么在我的代码中似乎有两个线程正在访问一个锁?

why does it seem like two threads are accessing one lock in my code?

我正在尝试学习 java 多线程,这是一个简单的 leetcode 并发问题 (https://leetcode.com/problems/print-foobar-alternately/)。我想出了以下代码,但我不明白它为什么有效。问题是一个线程调用 foo,一个线程调用 bar,它应该打印 "foobar" n 次。

public class Foobar {
    int n;
    boolean hasFoo;
    Lock lock;
    Condition cond;
    public Foobar(int n) {
        this.n = n;
        hasFoo = false;
        lock = new ReentrantLock();
        cond = lock.newCondition();
    }

    public void foo(Runnable printFoo) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            lock.lock();
            while (hasFoo) {
                cond.await();
            }
            printFoo.run();
            hasFoo = true;
            cond.signalAll();
            lock.unlock();
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            lock.lock();
            while (!hasFoo) {
                cond.await();
            }
            printBar.run();
            hasFoo = false;
            cond.signalAll();
            lock.unlock();
        }
    }
}

它有效,但我不明白为什么。根据我的理解,如果 "bar" 线程先运行并获得锁,它应该等待并且 "foo" 线程将阻塞在 lock.lock(); 行,但事实证明它们都进入了锁定部分。 java中我对lock的理解有误请赐教。 下面是我如何调用这两种方法。

        Foobar f = new Foobar(10);
        Runnable rf = () -> {
            try {
                f.foo(() -> System.out.println("foo"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
        Runnable rp = () -> {
            try {
                f.bar(() -> System.out.println("bar"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
        Thread t1 = new Thread(rf, "foo-thread");
        Thread t2 = new Thread(rp, "bar-thread");
        t1.start();
        t2.start();

她在 bar()Lock.lock() 中获取锁,但 Condition.await() 使线程等待并释放锁。

Condition.await() 指定(重点是我的):

Causes the current thread to wait until it is signalled or interrupted.

The lock associated with this Condition is atomically released and the current thread becomes disabled for thread scheduling purposes and lies dormant until one of four things happens:

所以假设 bar()foo() 之前执行:

public void bar(Runnable printBar) throws InterruptedException {
    // ...
    while (!hasFoo) { // true
      cond.await(); // release the lock and wait
    }
    // ...
}

因此 bar() 等待,但 foo() 进一步执行并打印 foo :

public void foo(Runnable printFoo) throws InterruptedException {
  //...
  while (hasFoo) { // false
      cond.await();
  }
  // We print first so
}

if the "bar" thread runs first and acquires the lock, it should wait ...

还等什么? "bar" 线程是第一个获得锁的线程,没有什么可等待的。该线程会立即执行下一条语句,即cond.await();释放锁1,线程进入休眠

与此同时,"foo"线程可以获得锁2,并打印它的消息,通知其他人它的工作完成了,随后,解锁3 睡眠 "bar".

... the "foo" thread will be blocked in the lock.lock();

完全正确。

but it turns out they both enter the locked part.

没有。一个进去了,另一个在等着进去。这是一个简单的锁 - 一次只有一个线程可以获取它(它就像 synchronized 语句的高级版本)。


1 The lock associated with this Condition is atomically released and the current thread becomes disabled for thread scheduling purposes and lies dormant

https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/concurrent/locks/Condition.html#await()

2 请记住,它被阻塞是因为它在 "bar" 完成后不久试图获取锁。

If the lock is not available then the current thread becomes disabled for thread scheduling purposes and lies dormant until the lock has been acquired.

https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/concurrent/locks/Lock.html#lock()

3 通过向所有人发出信号,the four methods 中的哪一个唤醒其他人。

  1. Some other thread invokes the signalAll() method for this Condition;

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html#await()