Java 生产者-消费者模式:按需线程

Java 生产者-消费者模式:按需线程,java,multithreading,Java,Multithreading,考虑以下类别: public class TaskWorkDemo { private final Object mLock = new Object(); private final ArrayDeque<String> mQueue = new ArrayDeque<String>(); private Thread mThread; private String getOne(){ synchronized (mLo

考虑以下类别:

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;
        }
    }
}
公共类TaskWorkDemo{
私有最终对象mLock=新对象();
private final ArrayDeque mQueue=new ArrayDeque();
私有线程mThread;
私有字符串getOne(){
已同步(mLock){
返回mQueue.isEmpty()?null:mQueue.peek();
}
}
//--产生--
私有void putOne(字符串s){
已同步(mLock){
多个报价;
}
//--在时间T--
if(mThread==null | |!mThread.isAlive()){
mThread=新线程(newrunner());
mThread.start();
}
}
私有类运行器实现可运行{
//--消耗--
@凌驾
公开募捐{
字符串s=getOne();
while(s!=null){
系统输出打印项次;
s=getOne();
}
//--在时间T--
mThread=null;
}
}
}
只有当队列中存在挂起的字符串时,使用者线程才应该存在,即不像我们看到的典型用法那样在队列中等待。所以,我试图在每次向队列中添加某个线程时创建一个线程,方法是检查之前的线程是否不存在或是否已完成

但这种方法有一个极端情况(请参见上文代码中时间T的
/--
):使用者线程已退出循环,但尚未完成。生产者线程将要检查前一个消费者线程是否仍然存在,它将发现它仍然在完成,并跳到创建一个新线程


有什么想法吗?

您可以使用JDK ThreadPoolExecutor。它允许您指定最小线程数(在您的情况下为零)、最大线程大小(在您的情况下为一个)和保持活动超时(队列为空时线程将挂起的时间)。

如果
putOne
被稀疏调用,则不会发生争用条件。如果它经常被调用,那么不应该使线程为空


(此答案假设这是一种练习,因为它显然不是在多线程环境中实现生产者-消费者算法的方法)

您不应该将
mThread
设置为null,因为它最终会导致
NullPointerException

  • thread1检查mThread==null,返回false
  • thread2设置
    mThread=null
  • thread1检查
    !mThread.isAlive()
    ,它抛出一个
    NullPointerException
  • 正如kan在回答中所建议的那样,您可能应该使用
    ThreadPoolExecutor
    来解决您的问题,但是如果出于某种原因您不能/不会这样做,那么您可以替换您的

    if(mThread == null || !mThread.isAlive())
    
    病况

    while(mThread.isAlive()) {
        sleep(sleep_parameter);
    }
    // mThread is no longer alive
    mThread = new Thread(new Runner());
    mThread.start();
    

    循环,它将循环直到线程终止。比睡眠更有效的替代方法是使用
    信号量
    ,以便使用者可以在其线程即将终止时发出信号(生产者将许可证为零的
    信号量
    传递给消费者,然后对信号量调用
    获取
    ,导致其阻塞;消费者在信号量即将终止时调用
    释放
    ,唤醒生产者)

    感谢您的回复。我认为,如果while loop每次都能升起一个标志,那么另一轮就要到期了,线程创建逻辑应该跳过创建新线程。当有两个线程时,会有一个短暂的时刻,但时间很短。它还应该是一个锁(或者至少是volatile关键字)在mThread重启前后,否则如果同时从多个线程调用
    putOne
    ,它可能会启动多个使用者线程。同时,while睡眠将锁定生产者线程,这可能也是不需要的。