为啥看起来两个线程正在访问我的代码中的一个锁?
Posted
技术标签:
【中文标题】为啥看起来两个线程正在访问我的代码中的一个锁?【英文标题】:why does it seem like two threads are accessing one lock in my code?为什么看起来两个线程正在访问我的代码中的一个锁? 【发布时间】:2019-07-26 08:08:42 【问题描述】:我正在尝试学习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中的锁。
我是这样称呼这两种方法的。
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();
【问题讨论】:
两个线程在哪里?他们是否在FooBar
的同一个实例上工作?因为如果他们使用单独的实例,他们也会获得单独的锁(每个实例都有自己的Lock lock
字段)。
向我们展示您如何实例化和运行线程。
为什么需要这些条件?仅仅锁定锁本身并不能实现什么?
【参考方案1】:
如果“bar”线程首先运行并获得锁,它应该等待......
等什么? “bar”线程是第一个获得锁的,没有什么可等待的。该线程将立即执行下一条语句,即cond.await();
,其中将释放锁1,线程将进入休眠状态。
与此同时,“foo”线程可以获取锁2,并打印它的消息,并通知其他人它的工作完成,随后解锁3 sup> 睡觉的“酒吧”。
... "foo" 线程将被阻塞在
lock.lock();
没错。
但事实证明他们都进入了锁定的部分。
没有。一个进了,一个等着进。这是一个简单的锁——一次只有一个线程可以获取它(就像synchronized
语句的高级版本)。
1 与此 Condition 关联的锁被原子释放,当前线程出于线程调度目的而被禁用并处于休眠状态
https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/concurrent/locks/Condition.html#await()
2 请记住,它被阻塞了,因为它在“bar”完成后不久就试图获取锁。
如果锁不可用,则当前线程将被禁用以进行线程调度,并处于休眠状态,直到获得锁为止。
https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/concurrent/locks/Lock.html#lock()
3 通过向所有人发出信号,the four methods 中的哪一个可以唤醒其他人。
其他一些线程为此
Condition
调用signalAll()
方法;https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html#await()
【讨论】:
这些条件的用例是什么?他们做了什么无法通过仅使用锁本身(这里和一般情况下)来实现? (这是一个诚实的问题,我从来没有见过以前用过的) 好的,Javadoc 解释了该部分:“条件将对象监控方法(等待、通知和 notifyAll)分解为不同的对象,以产生每个对象具有多个等待集的效果,通过将它们与任意 Lock 实现的使用结合起来。在 Lock 替换同步方法和语句的使用的地方,Condition 替换了 Object 监视器方法的使用。不过,这里似乎不需要。【参考方案2】:bar()
中的她,Lock.lock()
获取了锁,但Condition.await()
使线程等待并释放锁。
Condition.await()
指定(重点是我的):
使当前线程等待,直到它发出信号或 被打断了。
与此条件关联的锁被原子释放并且 当前线程因线程调度目的而被禁用,并且 处于休眠状态,直到发生以下四种情况之一:
所以假设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
【讨论】:
"与此条件关联的锁被原子释放"以上是关于为啥看起来两个线程正在访问我的代码中的一个锁?的主要内容,如果未能解决你的问题,请参考以下文章