ArrayBlockingQueue V.S. LinkedBlockingQueue



最近看《分布式JAVA应用 基础与实践》 里面有一段话
林昊 写道
ArrayBlockingQueue为一个固定大小数组、ReentrantLock以及Condition实现的可阻塞的先进先出的Queue。

除ArrayBlockingQueue之外,BlockingQueue的实现还有LinkedBlockingQueue,LinkedBlockingQueue实现的不同为采用对象的next构成链表的方式存储对象。由于读只操作对头,而写只操作队尾,这里巧妙地采用了两把锁,对于put和offer采用一把锁,对于take和poll则采用另外一把锁,避免了读写时互相竞争锁的情况,因此LinkedBlockingQueue在高并发读写操作都多的情况下,性能会较ArrayBlockingQueue好很多,在遍历以及删除元素则要两把锁都锁住。

乍一看,ArrayBlockingQueue怎么都不如LinkedBlockingQueue性能强大,那ArrayBlockingQueue存在的意义是什么呢?带着这个疑问,自己亲身写了个程序,4个生产者总共生产4* 102,4000*10个产品,4个消费者去消费,队列的最大大小是102,4000,队列分别采用ArrayBQ和LinkedBQ,对比2者消耗时间。(读者可以直接运行该程序得出对比结果)。


Java代码  收藏代码
import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.LinkedBlockingQueue; 
import java.util.concurrent.ArrayBlockingQueue; 
 
public class ArrayBlockingQueueVsLinkedBlockingQueue { 
    //队列最大容量 
    public static final int Q_SIZE = 1024000; 
    //生产者/消费者线程数 
    public static final int THREAD_NUM = 4; 
     
    //产品 
    class Product{ 
        String name; 
        Product(String name){ 
            this.name = name; 
        } 
    } 
    public void test(final BlockingQueue<Product> q) throws InterruptedException{ 
        //生产者线程 
        class Producer implements Runnable{ 
            @Override 
            public void run() { 
                for(int i=0;i<Q_SIZE*10;i++){ 
                    try { 
                        q.put(new Product("Lee")); 
                    } catch (InterruptedException e) { 
                        e.printStackTrace(); 
                    } 
                } 
            } 
             
        }; 
        //消费者线程 
        class Consumer implements Runnable{ 
            @Override 
            public void run(){ 
                for(int i=0;i<Q_SIZE*10;i++){ 
                    try { 
                        q.take(); 
                    } catch (InterruptedException e) { 
                        // TODO Auto-generated catch block 
                        e.printStackTrace(); 
                    } 
                } 
            } 
        }; 
        //创建生产者 
        Thread[] arrProducerThread = new Thread[THREAD_NUM]; 
        for(int i=0;i<THREAD_NUM;i++){ 
            arrProducerThread[i] = new Thread(new Producer()); 
        } 
        //创建消费者 
        Thread[] arrConsumerThread = new Thread[THREAD_NUM]; 
        for(int i=0;i<THREAD_NUM;i++){ 
            arrConsumerThread[i] = new Thread(new Consumer()); 
        } 
        //go! 
        long t1 = System.currentTimeMillis(); 
        for(int i=0;i<THREAD_NUM;i++){ 
            arrProducerThread[i].start(); 
            arrConsumerThread[i].start(); 
        } 
        for(int i=0;i<THREAD_NUM;i++){ 
            arrProducerThread[i].join(); 
            arrConsumerThread[i].join(); 
        } 
        long t2 = System.currentTimeMillis(); 
        System.out.println(q.getClass().getSimpleName() + " cost : " + (t2-t1)); 
    } 
    public static void main(String[] args) throws InterruptedException{ 
        final BlockingQueue<Product> q1 = new LinkedBlockingQueue<Product>(Q_SIZE); 
        final BlockingQueue<Product> q2 = new ArrayBlockingQueue<Product>(Q_SIZE); 
        new ArrayBlockingQueueVsLinkedBlockingQueue().test(q1); 
        new ArrayBlockingQueueVsLinkedBlockingQueue().test(q2); 
    } 




结果令人我意外,ArrayBQ耗时只有LinkedBQ的一半!而且运行的时候ArrayBQ没占满CPU,LinkedBQ却一直占满!


(我的机器 JDK1.6 win7_64 -Xms1024M -Xmx1024M)


(群友的机器)
我这就不懂了,锁竞争更小的LinkedBQ输给了ArrayBQ。。。难道是线程不够多?
于是我把参数改成以下
Java代码  收藏代码
//队列最大容量 
public static final int Q_SIZE = 10240; 
//生产者/消费者线程数 
public static final int THREAD_NUM = 1280; 

结果还是ArrayBQ更快。。。

各位技术的朋友,这究竟解释?如果您知道为什么,请务必留言告知鄙人!

我现在唯一的想到的是,LinkedQueue需要建立Node,而且增删时引用的操作比ArrayBQ复杂。ArrayBQ的内部的数组在增删时并不会产生复制的操作,而是当作一个环,增删只会改变putIndex和takeIndex,操作非常简单。

猜你喜欢

转载自yiduwangkai.iteye.com/blog/2292718