SynchronousQueue的疑问

luqing414 2015-11-04 09:30:41
缓冲队列采用 SynchronousQueue,它将任务直接交给线程处理而不保持它们。如果不存在可用于立即运行任务的线程(即线程池中的线程都在工作),则试图把任务加入缓冲队列将会失败,因此会构造一个新的线程来处理新添加的任务,并将其加入到线程池中。直接提交通常要求无界 maximumPoolSizes(Integer.MAX_VALUE) 以避免拒绝新提交的任务。newCachedThreadPool采用的便是这种策略。



这是网上找的关于SynchronousQueue的一段话,我的疑问有2个:
1. 他说的“缓冲队列采用 SynchronousQueue,它将任务直接交给线程处理而不保持它们”,这个不保持它们是什么意思,SynchronousQueue这个缓冲队列到底里面会存放任务吗,什么情况会存放,又什么时候被取出

2. 他说的“如果不存在可用于立即运行任务的线程(即线程池中的线程都在工作),则试图把任务加入缓冲队列将会失败”,这句话反过来理解,如果存在可用于立即运行任务的线程,那就会成功把任务加入SynchronousQueue吗?既然有空闲的线程,直接让线程运行任务不就完了吗,还需要把任务加入SynchronousQueue吗?

先在web论坛发了没人理,不知道是不是发错了地方,原帖见http://bbs.csdn.net/topics/391852458,知道的也可以在那边留言,一起结贴
...全文
260 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
luqing414 2015-11-04
  • 打赏
  • 举报
回复
引用 2 楼 rui888 的回复:
线程池也用到这个 。
我就是看了这个博客后才有的疑问,这个博客下面的评论中有不少人对作者的说法提出了质疑 先来看作者的描述: 例子一:使用直接提交策略,也即SynchronousQueue。 首先SynchronousQueue是无界的,也就是说他存数任务的能力是没有限制的,但是由于该Queue本身的特性,在某次添加元素后必须等待其他线程取走后才能继续添加。在这里不是核心线程便是新创建的线程,但是我们试想一样下,下面的场景。 我们使用一下参数构造ThreadPoolExecutor:

new ThreadPoolExecutor(
			2, 3, 30, TimeUnit.SECONDS, 
			new SynchronousQueue<Runnable>(), 
			new RecorderThreadFactory("CookieRecorderPool"), 
			new ThreadPoolExecutor.CallerRunsPolicy());
当核心线程已经有2个正在运行. 此时继续来了一个任务(A),根据前面介绍的“如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。”,所以A被添加到queue中。 又来了一个任务(B),且核心2个线程还没有忙完,OK,接下来首先尝试1中描述,但是由于使用的SynchronousQueue,所以一定无法加入进去。 此时便满足了上面提到的“如果无法将请求加入队列,则创建新的线程,除非创建此线程超出maximumPoolSize,在这种情况下,任务将被拒绝。”,所以必然会新建一个线程来运行这个任务。 暂时还可以,但是如果这三个任务都还没完成,连续来了两个任务,第一个添加入queue中,后一个呢?queue中无法插入,而线程数达到了maximumPoolSize,所以只好执行异常策略了。 完后下面是其它网友的质疑: 这里描述的场景不太对吧! SynchronousQueue队列是不会保存任何任务的。由于两个core线程都在忙,没有空闲线程等在SynchronousQueue队列的出口取任务,此时A任务的offer(e)操作一定是返回false的。所以ThreadPoolExecutor会再创建一个线程来承接A任务。等到B任务进来时,如果前面3个线程仍然都在忙,那么B任务就会因为当前线程数达到maximumPoolSize值,而被拒绝! 我的疑问:A任务到来的时候,到底是由ThreadPoolExecutor创建一个线程来承接A任务还是进了SynchronousQueue队列,按网友的说法,是不会进SynchronousQueue的,SynchronousQueue不会保存任何任务,如果是这样,这个队列有什么意义呢,它里面始终是空的吗
tony4geek 2015-11-04
  • 打赏
  • 举报
回复
tony4geek 2015-11-04
  • 打赏
  • 举报
回复
参考 具体看看源码 。SynchronousQueue 实现的是一种无锁的算法。 /** Dual Queue */ static final class TransferQueue extends Transferer 生产者生产过多的任务如果没有足够的消费者,会一直发阻塞。 举个例子数据库中你更新一条记录,同时另一个人也要来更新这条数据,他们维护自己的事务。
「已注销」 2015-11-04
  • 打赏
  • 举报
回复
用SynchronousQueue得明白SynchronousQueue的特性,它是一种阻塞队列,意味着一个put操作必须等待另一个take操作完成。它并不是一个容器,可以认为它是一个快速交换信息的通道。
tony4geek 2015-11-04
  • 打赏
  • 举报
回复
我的理解SynchronousQueue只要没有消费掉就不会产生。SynchronousQueue 的作用你可以理解为hadoff,可以看作线程的调度。 A blocking queue in which each put must wait for a take, and vice versa. A synchronous queue does not have any internal capacity, not even a capacity of one. You cannot peek at a synchronous queue because an element is only present when you try to take it; you cannot add an element (using any method) unless another thread is trying to remove it; you cannot iterate as there is nothing to iterate. The head of the queue is the element that the first queued thread is trying to add to the queue; if there are no queued threads then no element is being added and the head is null. For purposes of other Collection methods (for example contains), a SynchronousQueue acts as an empty collection. This queue does not permit null elements. 至于你说的保存任务,是在ThreadPoolExecutor 还是在SynchronousQueue. 有任务的时候是存在workQueue。 SynchronousQueue是BlockingQueue<Runnable>实现类
 /**
     * The queue used for holding tasks and handing off to worker
     * threads.  Note that when using this queue, we do not require
     * that workQueue.poll() returning null necessarily means that
     * workQueue.isEmpty(), so must sometimes check both. This
     * accommodates special-purpose queues such as DelayQueues for
     * which poll() is allowed to return null even if it may later
     * return non-null when delays expire.
     */
    private final BlockingQueue<Runnable> workQueue;
 /**
     * Executes the given task sometime in the future.  The task
     * may execute in a new thread or in an existing pooled thread.
     *
     * If the task cannot be submitted for execution, either because this
     * executor has been shutdown or because its capacity has been reached,
     * the task is handled by the current <tt>RejectedExecutionHandler</tt>.
     *
     * @param command the task to execute
     * @throws RejectedExecutionException at discretion of
     * <tt>RejectedExecutionHandler</tt>, if task cannot be accepted
     * for execution
     * @throws NullPointerException if command is null
     */
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
            if (runState == RUNNING && workQueue.offer(command)) {
                if (runState != RUNNING || poolSize == 0)
                    ensureQueuedTaskHandled(command);
            }
            else if (!addIfUnderMaximumPoolSize(command))
                reject(command); // is shutdown or saturated
        }
    }
其实每一个任务包装成一个Runnable类型的对象,执行 Runnable的run()方法。其次加入到线程池中也就是ThreadPoolExecutor中,主要的执行方法就是上面的execute(Runnable)方法。 任务满了之后就调用
 /**
     * Rechecks state after queuing a task. Called from execute when
     * pool state has been observed to change after queuing a task. If
     * the task was queued concurrently with a call to shutdownNow,
     * and is still present in the queue, this task must be removed
     * and rejected to preserve shutdownNow guarantees.  Otherwise,
     * this method ensures (unless addThread fails) that there is at
     * least one live thread to handle this task
     * @param command the task
     */
    private void ensureQueuedTaskHandled(Runnable command) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        boolean reject = false;
        Thread t = null;
        try {
            int state = runState;
            if (state != RUNNING && workQueue.remove(command))
                reject = true;
            else if (state < STOP &&
                     poolSize < Math.max(corePoolSize, 1) &&
                     !workQueue.isEmpty())
                t = addThread(null);
        } finally {
            mainLock.unlock();
        }
        if (reject)
            reject(command);
        else if (t != null)
            t.start();
    }

    /**
     * Invokes the rejected execution handler for the given command.
     */
    void reject(Runnable command) {
        handler.rejectedExecution(command, this);
    }
具体还是看看源码吧,个人见解。

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧