我什么时候应该使用SynchronousQueue

时间:2011-12-21 14:44:03

标签: java concurrency blockingqueue

new SynchronousQueue()
new LinkedBlockingQueue(1)

有什么区别?当我对容量为1的SynchronousQueue使用LinkedBlockingQueue时?

6 个答案:

答案 0 :(得分:47)

SynchronousQueue更像是一种切换,而LinkedBlockingQueue只允许一个元素。不同之处在于,对于SynchronousQueue 的put()调用将不会返回,直到有相应的take()调用,但是使用大小为1的LinkedBlockingQueue时,put()调用(到空队列) )将立即返回。

我不能说我曾经直接使用过SynchronousQueue,但它是用于Executors.newCachedThreadPool()方法的默认BlockingQueue。它本质上是BlockingQueue实现,当你真的想要一个队列时(你不想维护任何未决数据)。

答案 1 :(得分:9)

  

据我所知,上面的代码做同样的事情。

不,代码完全不一样。

Sync.Q。要求服务员提供成功。 LBQ将保留该项目,即使没有服务员也会立即完成报价。

SyncQ对于任务切换非常有用。想象一下,你有一个列表w /待处理任务和3个线程可用等待队列,尝试offer()列表的1/4,如果不接受线程可以自己运行任务。 [最后1/4应该由当前线程处理,如果你想知道为什么是1/4而不是1/3]

考虑尝试将任务交给工人,如果没有可用,您可以选择自己执行任务(或抛出异常)。相反w / LBQ,将任务留在队列中并不能保证任何执行。

注意:消费者和发布者的情况相同,即发布者可能阻止并等待消费者,但在offerpoll返回后,它确保处理任务/元素

答案 2 :(得分:6)

使用SynchronousQueue的一个原因是提高应用程序性能。如果必须在线程之间进行切换,则需要一些同步对象。如果您能满足其使用所需的条件,SynchronousQueue是我找到的最快的同步对象。其他人同意。请参阅:Implementation of BlockingQueue: What are the differences between SynchronousQueue and LinkedBlockingQueue

答案 3 :(得分:1)

[只是尝试用(可能)更清晰的词来表达。]

我相信SynchronousQueue API docs非常清楚地陈述了事情:

  
      
  1. 阻塞队列,其中每个插入操作必须等待另一个线程进行相应的删除操作,反之亦然。
  2.   
  3. 同步队列没有任何内部容量,甚至没有一个容量。您无法窥视同步队列,因为仅当您尝试删除它时,该元素才存在。您不能插入元素(使用任何方法),除非另一个线程试图将其删除;您无法迭代,因为没有要迭代的内容。
  4.   
  5. 队列头是第一个排队的插入线程试图添加到队列中的元素;如果没有这样的排队线程,则没有元素可用于删除,并且poll()将返回null
  6.   

还有BlockingQueue API docs

  
      
  1. 一个队列,它另外支持以下操作:在检索元素时等待队列变为非空,并在存储元素时等待队列中的空间变为可用。
  2.   

所以区别很明显,并且有些微妙,尤其是下面的第三点:

  1. 如果从BlockingQueue检索时队列为空,则操作块将一直插入直到插入新元素。另外,如果在插入BlockingQueue时队列已满,则该操作将阻塞,直到从队列中删除元素并为新队列留出空间为止。但是请注意,在SynchronousQueue中,由于操作被阻塞而在另一个线程上发生相反的操作(插入和删除彼此相反)。 因此,与BlockingQueue不同,阻塞取决于操作的存在,而不是元素的存在或不存在
  2. 由于阻塞取决于相反操作的存在,因此该元素永远不会真正插入队列中。这就是第二点的原因:“ 同步队列没有任何内部容量,甚至没有一个内部容量。
  3. 因此,peek()总是返回null(再次检查API doc),而iterator()返回一个空的迭代器,其中hasNext()总是返回{ {1}}。 (API doc)。但是,请注意,false方法会巧妙地检索并删除此队列的开头,如果当前有另一个线程正在使元素可用,并且如果不存在这样的线程,则它将返回poll()。 (API doc

最后,请注意,nullSynchronousQueue类都实现了LinkedBlockingQueue接口。

答案 4 :(得分:0)

SynchronousQueue以类似的方式工作,具有以下主要差异: 1)SynchronousQueue的大小为0 2)如果take()方法能够在同一时刻从队列中获取该元素,put()方法将只插入一个元素,即如果消费者take()调用需要一些时间才能插入元素消耗它。

SynchronousQueue - 仅在有人将在此时收到它时插入。

答案 5 :(得分:0)

基本上,同步队列用于切换目的。它们没有任何capacity,并且put操作被阻塞,直到其他线程执行get操作为止。

如果我们想在两个线程之间安全地共享一个变量,可以将该变量放入同步队列中,然后让其他线程从该队列中获取它。

https://www.baeldung.com/java-synchronous-queue中的代码示例

    ExecutorService executor = Executors.newFixedThreadPool(2);
    SynchronousQueue<Integer> queue = new SynchronousQueue<>();
Runnable producer = () -> {
    Integer producedElement = ThreadLocalRandom
      .current()
      .nextInt();
    try {
        queue.put(producedElement);
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }
};

Runnable consumer = () -> {
    try {
        Integer consumedElement = queue.take();
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }
};

executor.execute(producer);
executor.execute(consumer);

executor.awaitTermination(500, TimeUnit.MILLISECONDS);
executor.shutdown();
assertEquals(queue.size(), 0);

它们还用于CachedThreadPool中,以实现任务到达时无限创建(Integer.MAX)线程的效果。 带有同步队列的CachedPool的coreSize为0,maxPoolSize为Integer.MAX

随着任务进入队列,其他任务将被阻塞,直到第一个任务被取出。由于它没有任何队列容量,因此线程池将创建一个线程,并且该线程将执行任务,从而允许将更多任务放入队列。这将一直持续到线程创建达到maxPoolSize为止。根据超时,空闲线程可能会终止,并且新线程会创建,而不会超过maxPoolSize。