最近看《分布式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,操作非常简单。
林昊 写道
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,操作非常简单。