ArrayBlockingQueue 和 LinkedBlockingQueue 性能测试与分析

ArrayBlockingQueue 和 LinkedBlockingQueue 性能测试

测试代码:

package com.demo.queue;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class ArrayBlockingQueueVsLinkedBlockingQueue {
    //任务数量
    public static int TASK_SUM = 0;
    //生产者/消费者线程数
    public static int THREAD_NUM = 0;
    //队列大小
    public static int QUE_SIZE = 0;

    /**
     *测试方法
     */
    public void test(final BlockingQueue<String> q) throws InterruptedException {
        //生产者线程
        class Producer implements Runnable {
            @Override
            public void run() {
                for (int i = 0; i < TASK_SUM; i++) {
                    try {
                        q.put("task");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
        ;
        //消费者线程
        class Consumer implements Runnable {
            @Override
            public void run() {
                for (int i = 0; i < TASK_SUM; 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("任务数量="+ArrayBlockingQueueVsLinkedBlockingQueue.TASK_SUM);
        System.out.println("生产者和消费者各线程数量="+ArrayBlockingQueueVsLinkedBlockingQueue.THREAD_NUM);
        System.out.println("队列大小="+ArrayBlockingQueueVsLinkedBlockingQueue.QUE_SIZE);
        System.out.println(q.getClass().getSimpleName() + " 耗时 : " + (t2 - t1)+"ms");
        System.out.println();
    }

    /**
     *主方法
     */
    public static void main(String[] args) throws InterruptedException {
        //任务数量
        ArrayBlockingQueueVsLinkedBlockingQueue.TASK_SUM = 1000000;
        //生产者和消费者各多少线程
        ArrayBlockingQueueVsLinkedBlockingQueue.THREAD_NUM = 1;
        //队列大小
        ArrayBlockingQueueVsLinkedBlockingQueue.QUE_SIZE = 100;
        final BlockingQueue<String> q1 = new LinkedBlockingQueue<String>(ArrayBlockingQueueVsLinkedBlockingQueue.QUE_SIZE );
        final BlockingQueue<String> q2 = new ArrayBlockingQueue<String>(ArrayBlockingQueueVsLinkedBlockingQueue.QUE_SIZE );
        new ArrayBlockingQueueVsLinkedBlockingQueue().test(q1);
        new ArrayBlockingQueueVsLinkedBlockingQueue().test(q2);
    }
}

测试结果1:

任务数量=1000000
生产者和消费者各线程数量=1
队列大小=100
LinkedBlockingQueue 耗时 : 569ms

任务数量=1000000
生产者和消费者各线程数量=1
队列大小=100
ArrayBlockingQueue 耗时 : 560ms

测试结果2:

任务数量=1000000
生产者和消费者各线程数量=1
队列大小=1000
LinkedBlockingQueue 耗时 : 454ms

任务数量=1000000
生产者和消费者各线程数量=1
队列大小=1000
ArrayBlockingQueue 耗时 : 418ms

测试结果3:

任务数量=1000000
生产者和消费者各线程数量=10
队列大小=100
LinkedBlockingQueue 耗时 : 4253ms

任务数量=1000000
生产者和消费者各线程数量=10
队列大小=100
ArrayBlockingQueue 耗时 : 10053ms

测试结果4:

任务数量=1000000
生产者和消费者各线程数量=10
队列大小=1000
LinkedBlockingQueue 耗时 : 3655ms

任务数量=1000000
生产者和消费者各线程数量=10
队列大小=1000
ArrayBlockingQueue 耗时 : 3508ms

测试结果5:

任务数量=1000000
生产者和消费者各线程数量=30
队列大小=1000
LinkedBlockingQueue 耗时 : 10417ms

任务数量=1000000
生产者和消费者各线程数量=30
队列大小=1000
ArrayBlockingQueue 耗时 : 9432ms

测试结果6:

任务数量=1000000
生产者和消费者各线程数量=30
队列大小=5000
LinkedBlockingQueue 耗时 : 10449ms

任务数量=1000000
生产者和消费者各线程数量=30
队列大小=5000
ArrayBlockingQueue 耗时 : 5600ms

测试结果7:

任务数量=1000000
生产者和消费者各线程数量=100
队列大小=5000
LinkedBlockingQueue 耗时 : 33265ms

任务数量=1000000
生产者和消费者各线程数量=100
队列大小=5000
ArrayBlockingQueue 耗时 : 21303ms

总结:

  1. 总的来看,ArrayBlockingQueue 要比LinkedBlockingQueue 效率高;网上说LinkedBlockingQueue 性能更好,但我没测出来;
  2. 两者共同点,随着线程增多,并发越大,效率越低;
  3. 两者共同点,队列容量越大,效率越高;
  4. 两者共同点,单线程读写效率最高;

原因分析:

  1. 共同点好理解,线程多,并发大,互相争抢,协调资源需要时间,所以降低了效率。增加队列容量,相当于增大了缓冲,有益于提高性能;

  2. 但为什么ArrayBlockingQueue 性能更好,我没分析出来,有请大佬评论

修改代码,使生产者和消费者线程数量不同

package com.demo.queue;

import com.sun.deploy.util.StringUtils;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ArrayBlockingQueueVsLinkedBlockingQueue {
    //任务数量
    public static int TASK_SUM = 0;
    //生产者/消费者线程数
    public static int CONSUME_THREAD_NUM = 0;
    public static int PRODUCE_THREAD_NUM = 0;
    //队列大小
    public static int QUE_SIZE = 0;

    /**
     *测试方法
     */
    public void test(final BlockingQueue<String> queue) throws InterruptedException {
        //生产者线程
        class Producer implements Runnable {
            @Override
            public void run() {
                for (int i = 0; i < TASK_SUM; i++) {
                    try {
                        queue.put("task");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
        ;
        AtomicInteger counter = new AtomicInteger(0);
        //消费者线程
        class Consumer implements Runnable {

            @Override
            public void run() {
              while(true){
                  if(counter.get()>=TASK_SUM*PRODUCE_THREAD_NUM){
                      return;
                  }
                  try {
                      String poll = queue.poll(1, TimeUnit.MICROSECONDS);
                      if(poll!=null){
                          counter.incrementAndGet();
                      }

                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
            }
        }
        ;
        //创建生产者
        Thread[] arrProducerThread = new Thread[PRODUCE_THREAD_NUM];
        for (int i = 0; i < PRODUCE_THREAD_NUM; i++) {
            arrProducerThread[i] = new Thread(new Producer());
        }
        //创建消费者
        Thread[] arrConsumerThread = new Thread[CONSUME_THREAD_NUM];
        for (int i = 0; i < CONSUME_THREAD_NUM; i++) {
            arrConsumerThread[i] = new Thread(new Consumer());
        }
        //go!
        long t1 = System.currentTimeMillis();
        for (int i = 0; i < CONSUME_THREAD_NUM; i++) {
            arrConsumerThread[i].start();
        }
        for (int i = 0; i < PRODUCE_THREAD_NUM; i++) {
            arrProducerThread[i].start();
        }
        //等待完成
        for (int i = 0; i < CONSUME_THREAD_NUM; i++) {
            arrConsumerThread[i].join();
        }
        for (int i = 0; i < PRODUCE_THREAD_NUM; i++) {
            arrProducerThread[i].join();
        }
        long t2 = System.currentTimeMillis();
        System.out.println("任务数量="+TASK_SUM);
        System.out.println("生产者各线程数量="+PRODUCE_THREAD_NUM);
        System.out.println("消费者各线程数量="+CONSUME_THREAD_NUM);
        System.out.println("队列大小="+QUE_SIZE);
        System.out.println("执行完毕的任务数量="+counter.get());
        System.out.println(queue.getClass().getSimpleName() + " 耗时 : " + (t2 - t1)+"ms");
        System.out.println();
    }

    /**
     *主方法
     */
    public static void main(String[] args) throws InterruptedException {
        //任务数量
        ArrayBlockingQueueVsLinkedBlockingQueue.TASK_SUM = 1000000;
        //生产者和消费者各多少线程
        ArrayBlockingQueueVsLinkedBlockingQueue.PRODUCE_THREAD_NUM = 1;
        ArrayBlockingQueueVsLinkedBlockingQueue.CONSUME_THREAD_NUM = 1;
        //队列大小
        ArrayBlockingQueueVsLinkedBlockingQueue.QUE_SIZE = 100;
        final BlockingQueue<String> q1 = new LinkedBlockingQueue<String>(ArrayBlockingQueueVsLinkedBlockingQueue.QUE_SIZE );
        final BlockingQueue<String> q2 = new ArrayBlockingQueue<String>(ArrayBlockingQueueVsLinkedBlockingQueue.QUE_SIZE );

        new ArrayBlockingQueueVsLinkedBlockingQueue().test(q1);
        new ArrayBlockingQueueVsLinkedBlockingQueue().test(q2);
    }
}

测试结果1:
都是单线程,ArrayBlockingQueue 相对较快

任务数量=1000000
生产者各线程数量=1
消费者各线程数量=1
队列大小=100
执行完毕的任务数量=1000000
LinkedBlockingQueue 耗时 : 267ms

任务数量=1000000
生产者各线程数量=1
消费者各线程数量=1
队列大小=100
执行完毕的任务数量=1000000
ArrayBlockingQueue 耗时 : 203ms

测试结果2:
一个生产者,10个消费者,LinkedBlockingQueue耗时稍微上涨。ArrayBlockingQueue 耗时上涨较多。

任务数量=1000000
生产者各线程数量=1
消费者各线程数量=10
队列大小=100
执行完毕的任务数量=1000000
LinkedBlockingQueue 耗时 : 321ms

任务数量=1000000
生产者各线程数量=1
消费者各线程数量=10
队列大小=100
执行完毕的任务数量=1000000
ArrayBlockingQueue 耗时 : 919ms

测试结果3:
10个生产者,10个消费者,队列大小100,耗时都剧增,ArrayBlockingQueue 耗时是LinkedBlockingQueue 的3倍。

任务数量=1000000
生产者各线程数量=10
消费者各线程数量=10
队列大小=100
执行完毕的任务数量=10000000
LinkedBlockingQueue 耗时 : 2208ms

任务数量=1000000
生产者各线程数量=10
消费者各线程数量=10
队列大小=100
执行完毕的任务数量=10000000
ArrayBlockingQueue 耗时 : 6620ms

测试结果4:
10个生产者,10个消费者,加大队列容量到1000,两者耗时相对之前有所下降。ArrayBlockingQueue 耗时较少。

任务数量=1000000
生产者各线程数量=10
消费者各线程数量=10
队列大小=1000
执行完毕的任务数量=10000000
LinkedBlockingQueue 耗时 : 1860ms

任务数量=1000000
生产者各线程数量=10
消费者各线程数量=10
队列大小=1000
执行完毕的任务数量=10000000
ArrayBlockingQueue 耗时 : 1459ms

测试结果5:
10个生产者,20个消费者,队列容量还是1000。LinkedBlockingQueue 耗时基本不变。ArrayBlockingQueue 耗时增加,且大于LinkedBlockingQueue ;

任务数量=1000000
生产者各线程数量=10
消费者各线程数量=20
队列大小=1000
执行完毕的任务数量=10000000
LinkedBlockingQueue 耗时 : 1845ms

任务数量=1000000
生产者各线程数量=10
消费者各线程数量=20
队列大小=1000
执行完毕的任务数量=10000000
ArrayBlockingQueue 耗时 : 2100ms

测试结果6:
20个生产者,10个消费者,队列大小还是1000。两者耗时都剧增。ArrayBlockingQueue 耗时更大。

任务数量=1000000
生产者各线程数量=20
消费者各线程数量=10
队列大小=1000
执行完毕的任务数量=20000000
LinkedBlockingQueue 耗时 : 3752ms

任务数量=1000000
生产者各线程数量=20
消费者各线程数量=10
队列大小=1000
执行完毕的任务数量=20000000
ArrayBlockingQueue 耗时 : 5121ms

总结:
LinkedBlockingQueue 更适合生产者数量少于消费者的情况;
ArrayBlockingQueue 更适合队列容量大的情况;
生产者数量少于消费者时候,两者效率都会更高。
并发越小,两者效率越高。

你可能感兴趣的:(Java,队列,多线程,java,并发编程)