考虑以下课程:
public class TaskWorkDemo {
private final Object mLock = new Object();
private final ArrayDeque<String> mQueue = new ArrayDeque<String>();
private Thread mThread;
private String getOne(){
synchronized (mLock){
return mQueue.isEmpty() ? null : mQueue.peek();
}
}
//--produce--
private void putOne(String s){
synchronized (mLock){
mQueue.offer(s);
}
//-- at time T --
if(mThread == null || !mThread.isAlive()){
mThread = new Thread(new Runner());
mThread.start();
}
}
private class Runner implements Runnable{
//--consume--
@Override
public void run() {
String s = getOne();
while (s != null){
System.out.println(s);
s = getOne();
}
//-- at time T --
mThread = null;
}
}
}
只有当队列中有待处理的字符串时才会存在消费者线程,即不像我们看到的典型用法那样等待队列。所以,每次通过检查是否有任何先前的线程不存在或已经完成时,我都会尝试创建一个线程。
但是这种方法有一个极端情况(参见上面代码中的//-- at time T --
):消费者线程不在循环中,但尚未完成。生产者线程即将检查以前的消费者线程是否仍在,它会发现它仍然整理,并跳过创建一个新的。
有什么想法吗?
答案 0 :(得分:1)
您可以使用JDK ThreadPoolExecutor。它允许您指定最小线程数(在您的情况下为零),最大线程大小(在您的情况下为一个)和保持活动超时(线程将在队列为空时挂起的时间)。
答案 1 :(得分:1)
您不应将mThread
设为null,因为它最终会产生NullPointerException
:
mThread == null
,返回false mThread = null
!mThread.isAlive()
,它会抛出NullPointerException
你可能应该使用ThreadPoolExecutor
来解决你的问题,正如kan在他的回答中所建议的那样,但如果由于某种原因你无法做到这一点,那么你可以替换你的1} p>
if(mThread == null || !mThread.isAlive())
带有
的条件while(mThread.isAlive()) {
sleep(sleep_parameter);
}
// mThread is no longer alive
mThread = new Thread(new Runner());
mThread.start();
循环,循环直到线程终止。比睡眠更有效的替代方法是使用类似Semaphore
的东西,以便消费者可以在其线程即将终止时发出信号(生产者将零Semaphore
传递给消费者,然后调用{导致它阻塞的信号量上的{1}};当信号量即将终止时,消费者会在信号量上调用acquire
,这会唤醒生产者)
答案 2 :(得分:0)
如果稀疏地调用putOne
,则不会发生竞争条件。如果它经常被调用,那么你不应该使线程无效。
(这个答案假设这是一种练习,因为它显然不是在多线程环境中实现生产者 - 消费者算法的方法)