让一个线程进入休眠状态,直到另一个线程解决了某个条件
这里有两段代码完成(我认为是)同样的事情.
Here are two chunks of code that accomplish (what I think is) the same thing.
我基本上是在尝试学习如何使用 Java 1.5 的并发来摆脱 Thread.sleep(long).第一个示例使用 ReentrantLock,第二个示例使用 CountDownLatch.我想要做的主要是让一个线程进入睡眠状态,直到另一个线程解决了一个条件.
I basically am trying to learn how to use Java 1.5's concurrency to get away from Thread.sleep(long). The first example uses ReentrantLock, and the second example uses CountDownLatch. The jist of what I am trying to do is put one thread to sleep until a condition is resolved in another thread.
ReentrantLock 为我用来决定是否唤醒另一个线程的布尔值提供一个锁,然后我使用带有等待/信号的 Condition 来休眠另一个线程.据我所知,我需要使用锁的唯一原因是如果多个线程需要对布尔值的写访问.
The ReentrantLock provides a lock on the boolean I'm using to decide whether to wake the other thread or not, and then I use the Condition with await/signal to sleep the other thread. As far as I can tell, the only reason I would need to use locks is if more than one thread required write access to the boolean.
CountDownLatch 似乎提供与 ReentrantLock 相同的功能,但没有(不必要的?)锁.然而,感觉就像我通过只需要一个倒计时来初始化它来劫持它的预期用途.我认为它应该在多个线程要处理同一个任务时使用,而不是在多个线程正在等待一个任务时使用.
The CountDownLatch seems to provide the same functionality as the ReentrantLock but without the (unnecessary?) locks. However, it feels like I am kind of hijacking its intended use by initializing it with only one countdown necessary. I think it's supposed to be used when multiple threads are going to be working on the same task, not when multiple threads are waiting on one task.
所以,问题:
我是否在 ReentrantLock 代码中为正确的事情"使用了锁?如果我只在一个线程中写入布尔值,是否需要锁?只要我在唤醒任何其他线程之前重置布尔值就不会导致问题,可以吗?
Am I using locks for "the right thing" in the ReentrantLock code? If I am only writing to the boolean in one thread, are the locks necessary? As long as I reset the boolean before waking up any other threads I can't cause a problem, can I?
是否有一个类似于 CountDownLatch 的类可以用来避免更自然地适合此任务的锁(假设我应该在这种情况下避免它们)?
Is there a class similar to CountDownLatch I can use to avoid locks (assuming I should be avoiding them in this instance) that is more naturally suited to this task?
还有其他方法可以改进我应该注意的代码吗?
Are there any other ways to improve this code I should be aware of?
示例一:
import java.util.concurrent.locks.*;
public class ReentrantLockExample extends Thread {
//boolean - Is the service down?
boolean serviceDown;
// I am using this lock to synchronize access to sDown
Lock serviceLock;
// and this condition to sleep any threads waiting on the service.
Condition serviceCondition;
public static void main(String[] args) {
Lock l = new ReentrantLock();
Condition c = l.newCondition();
ReentrantLockExample rle = new ReentrantLockExample(l, c);
//Imagine this thread figures out the service is down
l.lock();
try {
rle.serviceDown = true;
} finally {
l.unlock();
}
int waitTime = (int) (Math.random() * 5000);
System.out.println("From main: wait time is " + waitTime);
rle.start();
try {
//Symbolizes some random time that the service takes to come back up.
Thread.sleep(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Imagine this thread figures out that the service is back up.
l.lock();
try {
rle.serviceDown = false;
c.signal();
} finally {
l.unlock();
}
}
//Constructor
public ReentrantLockExample(Lock l, Condition c) {
this.serviceLock = l;
this.serviceCondition = c;
}
/*
* Should wait for this imaginary service to come back online.
*/
public void run() {
System.out.println("Thread: start awaiting");
serviceLock.lock();
try {
while (isServiceDown())
{
serviceCondition.await();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
serviceLock.unlock();
}
System.out.println("Thread: done awaiting");
}
private boolean isServiceDown() {
return serviceDown;
}
}
示例二:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;
public class CountDownLatchExample extends Thread {
//boolean - Is the service down?
boolean serviceDown;
// I am using this latch to wait on the service.
CountDownLatch serviceLatch;
public static void main(String[] args) {
CountDownLatch cdl = new CountDownLatch(1);
CountDownLatchExample cdle = new CountDownLatchExample(cdl);
//Service goes down.
cdle.serviceDown = true;
int waitTime = (int) (Math.random() * 5000);
System.out.println("From main: wait time is " + waitTime);
cdle.start();
try {
//Symbolizes some random time that the service takes to come back up.
Thread.sleep(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Service comes back up.
cdle.serviceDown = false;
cdl.countDown();
}
//Constructor
public CountDownLatchExample(CountDownLatch cdl) {
this.serviceLatch = cdl;
}
/*
* Should wait for this imaginary service to come back online.
*/
public void run() {
System.out.println("Thread: start awaiting");
try {
while (isServiceDown()) {
serviceLatch.await();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Thread: done awaiting");
}
private boolean isServiceDown() {
return serviceDown;
}
}
推荐答案
这两种方法大致等效,除了 CountDownLatch 只能释放一次.之后所有 await() 调用立即返回.所以 CyclicBarrier 实际上可能更多如果您使用的服务可能会下降和上升,则适用.
Either approach is roughly equivalent, except that a CountDownLatch can only be released once. After that all await() calls return instantly. So a CyclicBarrier may actually be more appropriate if you are working with a service that may go down and up.
如果您的条件确实是一次性交易,那么 FutureTask 会更合适.您可以调用 get() 这将等待服务可用,然后您可以在 get() 返回后立即使用该服务.
If your condition really is a one-shot deal, then a FutureTask would be more appropriate. You could call get() which would wait for the service to become available, and then you could use the service as soon as get() returns.
您提到 CountDownLatch 允许在不使用锁的情况下等待.但是,CountDownLatch 和 ReentrantLock 使用 AbstractQueuedSynchronizer 实现.在底层,它们提供相同的同步和可见性语义.
You mention that CountDownLatch allows waiting without using Locks. However, both CountDownLatch and ReentrantLock are implemented using AbstractQueuedSynchronizer. Under the hood, they provide identical synchronization and visibility semantics.
相关文章