Java BlockingQueue的实现:SynchronousQueue和LinkedBlockingQueue之间有什么区别
我看到了这些BlockingQueue的实现,无法理解它们之间的区别。到目前为止,我的结论是:Java BlockingQueue的实现:SynchronousQueue和LinkedBlockingQueue之间有什么区别,java,collections,concurrency,java.util.concurrent,Java,Collections,Concurrency,Java.util.concurrent,我看到了这些BlockingQueue的实现,无法理解它们之间的区别。到目前为止,我的结论是: 我永远不需要同步队列 LinkedBlockingQueue确保先进先出,必须使用参数true创建阻塞队列,使其成为先进先出 SynchronousQueue中断大多数收集方法(包含、大小等) 那么我什么时候需要同步队列呢?此实现的性能是否优于LinkedBlockingQueue 让事情变得更复杂。。。当其他(Executors.newSingleThreadExecutor和Executors.n
我得到的是,使用SynchronousQueue,如果没有空闲线程,生产者将被阻止。但是由于线程的数量实际上是无限的(如果需要,将创建新的线程),这将永远不会发生。那么为什么要使用SynchronousQueue呢?缓存线程池会根据需要创建线程。它需要一个队列,该队列要么将任务传递给等待的使用者,要么失败。如果没有等待的使用者,它将创建一个新线程。SynchronousQueue不保存元素,而是传递元素或失败。
SynchronousQueue
是一种非常特殊的队列-它在队列
接口后面实现会合方法(生产者等待消费者准备好,消费者等待生产者准备好)
因此,您可能只在需要特定语义的特殊情况下才需要它,例如
使用同步队列的另一个原因是性能。SynchronousQueue
的实现似乎得到了极大的优化,因此,如果您只需要一个集合点(如Executors.newCachedThreadPool()
,在这里“按需”创建使用者,这样队列项就不会累积),您可以通过使用SynchronousQueue
获得性能增益
简单综合测试表明,在一个简单的单生产者-单消费者场景中,SynchronousQueue
的双核机器吞吐量比队列长度为1的LinkedBlockingQueue
和ArrayBlockingQueue
的吞吐量高约20倍。当队列长度增加时,它们的吞吐量增加,几乎达到SynchronousQueue
的吞吐量。这意味着与其他队列相比,SynchronousQueue
在多核机器上具有较低的同步开销。但同样,只有在特定情况下,当您需要伪装成队列的集合点时,它才起作用
编辑:
下面是一个测试:
public class Test {
static ExecutorService e = Executors.newFixedThreadPool(2);
static int N = 1000000;
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
int length = (i == 0) ? 1 : i * 5;
System.out.print(length + "\t");
System.out.print(doTest(new LinkedBlockingQueue<Integer>(length), N) + "\t");
System.out.print(doTest(new ArrayBlockingQueue<Integer>(length), N) + "\t");
System.out.print(doTest(new SynchronousQueue<Integer>(), N));
System.out.println();
}
e.shutdown();
}
private static long doTest(final BlockingQueue<Integer> q, final int n) throws Exception {
long t = System.nanoTime();
e.submit(new Runnable() {
public void run() {
for (int i = 0; i < n; i++)
try { q.put(i); } catch (InterruptedException ex) {}
}
});
Long r = e.submit(new Callable<Long>() {
public Long call() {
long sum = 0;
for (int i = 0; i < n; i++)
try { sum += q.take(); } catch (InterruptedException ex) {}
return sum;
}
}).get();
t = System.nanoTime() - t;
return (long)(1000000000.0 * N / t); // Throughput, items/sec
}
}
公共类测试{
静态ExecutorService e=Executors.newFixedThreadPool(2);
静态整数N=1000000;
公共静态void main(字符串[]args)引发异常{
对于(int i=0;i<10;i++){
整数长度=(i==0)?1:i*5;
系统输出打印(长度+“\t”);
System.out.print(doTest(新的LinkedBlockingQueue(长度),N)+“\t”);
System.out.print(doTest(新的ArrayBlockingQueue(长度),N)+“\t”);
System.out.print(doTest(newsynchronousqueue(),N));
System.out.println();
}
e、 关机();
}
私有静态长点测试(final BlockingQueue q,final int n)引发异常{
long t=System.nanoTime();
e、 提交(新的Runnable(){
公开募捐{
对于(int i=0;i
这是我机器上的一个结果:
当前默认的执行器
(基于ThreadPoolExecutor
)可以使用一组固定大小的预创建线程和一定大小的BlockingQueue
用于任何溢出,或者在(且仅当)队列已满时创建最大大小的线程
这导致了一些令人惊讶的特性。例如,由于附加线程仅在达到队列容量后创建,因此使用LinkedBlockingQueue
(无边界)意味着永远不会创建新线程,即使当前池大小为零。如果使用ArrayBlockingQueue
,则仅当新线程已满时才会创建新线程,并且如果此时池尚未清除空间,则后续作业很可能会被拒绝
SynchronousQueue
的容量为零,因此生产者阻塞,直到消费者可用或创建线程。这意味着,尽管@axtavt产生了令人印象深刻的数据,但从生产者的角度来看,缓存线程池通常具有最差的性能
不幸的是,目前还没有一个很好的折衷实现的库版本,它可以在突发事件或活动期间创建线程,从最小值到最大值