Java BlockingQueue的实现:SynchronousQueue和LinkedBlockingQueue之间有什么区别

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

我看到了这些BlockingQueue的实现,无法理解它们之间的区别。到目前为止,我的结论是:

  • 我永远不需要同步队列
  • LinkedBlockingQueue确保先进先出,必须使用参数true创建阻塞队列,使其成为先进先出
  • SynchronousQueue中断大多数收集方法(包含、大小等)
  • 那么我什么时候需要同步队列呢?此实现的性能是否优于LinkedBlockingQueue

    让事情变得更复杂。。。当其他(Executors.newSingleThreadExecutorExecutors.newFixedThreadPool)使用LinkedBlockingQueue时,为什么Executors.newCachedThreadPool使用SynchronousQueue

    编辑

    第一个问题解决了。但是我仍然不明白为什么其他(Executors.newSingleThreadExecutorExecutors.newFixedThreadPool)使用LinkedBlockingQueue时,Executors.newCachedThreadPool使用SynchronousQueue


    我得到的是,使用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产生了令人印象深刻的数据,但从生产者的角度来看,缓存线程池通常具有最差的性能

    不幸的是,目前还没有一个很好的折衷实现的库版本,它可以在突发事件或活动期间创建线程,从最小值到最大值