3 回答
TA贡献1810条经验 获得超4个赞
同步有两个主要用例:资源访问和事件传递。您试图解决的并发问题需要事件传递:第二个线程等待第一个线程的信号,第三个线程等待第二个线程的信号。信号,我的意思是,是空事件,它们带有事件已经发生的事实,没有任何额外的细节。
Semaphore
s 非常适合信号传递(尽管也可用于资源访问)。ReentrantLock
s 是为资源访问而设计的。从技术上讲,任何事件传递机制都是建立在资源访问之上的。因此您可以使用ReentrantLock
s 进行信号传递,但它需要一些额外的编程(在@billie 的回答中演示)。
TA贡献2011条经验 获得超2个赞
正如您所发现的,信号量易于实现和推理 - 获取锁定、执行操作、释放锁定。ReentrantLocks 是不同的,旨在服务于您发布的示例中不存在的更复杂的目的。是的,ReentrantLocks 由单个线程拥有,只能由该线程释放。
如果您的目标是生成供两个线程使用的受保护代码,则使用信号量会比 ReentrantLock 更简单且更不容易出错。但是,如果您的目标是了解 ReentrantLocks 的工作原理(或者如果您的用例以某种方式更适合 ReentrantLocks 而不是从上面的示例中清楚地看到),我鼓励您阅读 Javadoc - https://docs.oracle.com/ javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html - 它包含有关如何使用它们以及它们的行为方式的有用信息。
TA贡献1815条经验 获得超6个赞
ReentrantLock解锁状态的API将抛出IllegalMonitorStateException - if the current thread does not hold this lock,在您的代码中,这看起来正是正在发生的事情。
这不是ReentrantLocks 的正常用法,但是如果您无论如何都想使用它们来完成此操作,那么一种方法可能是使用以下方法跟踪状态:
private volatile int tracker = 0;
private Lock lock= new ReentrantLock();
public void first(Runnable printFirst) throws InterruptedException {
lock.lock();
try {
printFirst.run();
tracker++;
} finally {
lock.unlock();
}
}
public void second(Runnable printSecond) throws InterruptedException {
while(!Thread.currentThread().isInterrupted()) {
lock.lock();
try {
if (tracker == 1) {
printSecond.run();
tracker++;
break;
}
} finally {
lock.unlock();
}
Thread.yield();
}
}
public void third(Runnable printThird) throws InterruptedException {
while(!Thread.currentThread().isInterrupted()) {
lock.lock();
try {
if (tracker == 2) {
printThird.run();
tracker++;
break;
}
} finally {
lock.unlock();
}
Thread.yield();
}
}
如果您希望它更高效(减少第二个/第三个函数的处理),那么您可能需要查看类似Condition https://docs.oracle.com/javase/7/docs/api/java/util/concurrent的内容/锁/Condition.html
作为旁注,您可以通过为每个操作要求/释放多个许可来仅使用一个信号量来完成此操作。
添加回答
举报