61
new SynchronousQueue()
new LinkedBlockingQueue(1)

有什么区别?我什么时候应该使用容量为 1 的SynchronousQueue反对?LinkedBlockingQueue

4

6 回答 6

60

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

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

于 2011-12-21T14:49:04.113 回答
10

据我了解,上面的代码做同样的事情。

不,代码根本不一样。

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

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

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

注意:消费者和发布者的情况是一样的,即发布者可以阻塞并等待消费者,但在offerpoll返回之后,它确保任务/元素被处理。

于 2011-12-21T18:49:05.980 回答
7

使用 SynchronousQueue 的原因之一是提高应用程序性能。如果您必须在线程之间进行切换,您将需要一些同步对象。如果你能满足使用它所需要的条件,SynchronousQueue 是我找到的最快的同步对象。其他人同意。参见:BlockingQueue的实现:SynchronousQueue和LinkedBlockingQueue有什么区别

于 2013-11-01T00:43:29.223 回答
1

SynchronousQueue 以类似的方式工作,但有以下主要区别: 1) SynchronousQueue 的大小为 0 2) put() 方法将仅在 take() 方法能够同时从队列中获取该元素时插入一个元素,即如果消费者 take() 调用将花费一些时间来使用元素,则无法插入该元素。

SynchronousQueue - 仅当有人在那个时刻收到它时才插入。

于 2015-01-13T14:26:07.100 回答
1

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

我相信SynchronousQueueAPI 文档非常清楚地说明了事情:

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

BlockingQueueAPI 文档

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

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

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

最后,一个小说明,SynchronousQueueLinkedBlockingQueue类都实现了BlockingQueue接口。

于 2018-10-18T17:04:30.360 回答
0

同步队列主要用于切换目的。它们没有任何容量,并且 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。根据 timeOut,空闲线程可能会被终止,并在不超过 maxPoolSize 的情况下创建新线程。

于 2019-12-25T14:33:30.047 回答