【问题标题】:Producer consumer pattern : on demand thread生产者消费者模式:按需线程
【发布时间】:2013-08-05 19:04:51
【问题描述】:

考虑以下类:

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 --):消费者线程已退出循环,但尚未完成。生产者线程即将检查之前的消费者线程是否还在,它会发现它还在结束,然后跳过创建一个新的。

有什么想法吗?

【问题讨论】:

    标签: java multithreading


    【解决方案1】:

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

    1. thread1 检查mThread == null,返回false
    2. thread2组mThread = null
    3. thread1 检查!mThread.isAlive(),它会抛出一个NullPointerException

    您可能应该使用ThreadPoolExecutor 来解决您的问题,正如 kan 在他的回答中所建议的那样,但如果由于某种原因您不能/不会这样做,那么您可以更换您的

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

    条件与 a

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

    loop,循环直到线程终止。比休眠更有效的替代方法是使用Semaphore 之类的东西,以便消费者可以在其线程即将终止时发出信号(生产者将零许可的Semaphore 传递给消费者,然后调用acquire导致它阻塞的信号量;然后消费者在信号量即将终止时调用release,这会唤醒生产者)

    【讨论】:

    • 感谢您的回复。我认为如果 while 循环每次都能够引发一个标志,那么另一轮就到期了,线程创建逻辑应该跳过创建一个新线程。有两个线程时会有短暂的时刻,但只是很短的时间。
    • 它也应该是mThread重启的一个锁(或者至少是volatile关键字),否则如果putOne从多个线程同时调用,它可能会启动多个消费者线程。此外,while-sleep 将锁定生产者线程,这可能也是不受欢迎的。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      如果putOne 被稀疏调用,则不会发生竞争条件。如果它被频繁调用,那么你不应该取消该线程。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-01-22
        • 1970-01-01
        • 2018-09-24
        • 2017-02-01
        • 2012-04-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多