您的位置:首页 > 产品设计 > UI/UE

(原创)JAVA阻塞队列LinkedBlockingQueue 以及非阻塞队列ConcurrentLinkedQueue 的区别

2015-11-26 16:10 686 查看
阻塞队列:线程安全

按FIFO(先进先出)排序元素。队列的头部是在队列中时间最长的元素。队列的尾部是在队列中时间最短的元素。新元素插入到队列的尾部,并且队列检索操作会获得位于队列头部的元素。链接队列的吞吐量通常要高于基于数组的队列,但是在大多数并发应用程序中,其可预知的性能要低。

注意:

1、必须要使用take()方法在获取的时候达成阻塞结果
2、使用poll()方法将产生非阻塞效果



importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.LinkedBlockingDeque;
importjava.util.concurrent.LinkedBlockingQueue;
importjava.util.concurrent.TimeUnit;

publicclassBlockingDeque{
//阻塞队列,FIFO
privatestaticLinkedBlockingQueue<Integer>concurrentLinkedQueue=newLinkedBlockingQueue<Integer>();

publicstaticvoidmain(String[]args){
ExecutorServiceexecutorService=Executors.newFixedThreadPool(2);

executorService.submit(newProducer("producer1"));
executorService.submit(newProducer("producer2"));
executorService.submit(newProducer("producer3"));
executorService.submit(newConsumer("consumer1"));
executorService.submit(newConsumer("consumer2"));
executorService.submit(newConsumer("consumer3"));

}

staticclassProducerimplementsRunnable{
privateStringname;

publicProducer(Stringname){
this.name=name;
}

publicvoidrun(){
for(inti=1;i<10;++i){
System.out.println(name+"生产:"+i);
//concurrentLinkedQueue.add(i);
try{
concurrentLinkedQueue.put(i);
Thread.sleep(200);//模拟慢速的生产,产生阻塞的效果
}catch(InterruptedExceptione1){
//TODOAuto-generatedcatchblock
e1.printStackTrace();
}

}
}
}

staticclassConsumerimplementsRunnable{
privateStringname;

publicConsumer(Stringname){
this.name=name;
}
publicvoidrun(){
for(inti=1;i<10;++i){
try{
//必须要使用take()方法在获取的时候阻塞
System.out.println(name+"消费:"+concurrentLinkedQueue.take());
//使用poll()方法将产生非阻塞效果
//System.out.println(name+"消费:"+concurrentLinkedQueue.poll());

//还有一个超时的用法,队列空时,指定阻塞时间后返回,不会一直阻塞
//但有一个疑问,既然可以不阻塞,为啥还叫阻塞队列?
//System.out.println(name+"Consumer"+concurrentLinkedQueue.poll(300,TimeUnit.MILLISECONDS));
}catch(Exceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}

}
}
}
}






非阻塞队列

基于链接节点的、无界的、线程安全。此队列按照FIFO(先进先出)原则对元素进行排序。队列的头部是队列中时间最长的元素。队列的尾部是队列中时间最短的元素。新的元素插入到队列的尾部,队列检索操作从队列头部获得元素。当许多线程共享访问一个公共collection时,ConcurrentLinkedQueue是一个恰当的选择。此队列不允许null元素。

例子

importjava.util.concurrent.ConcurrentLinkedQueue;
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.LinkedBlockingDeque;
importjava.util.concurrent.TimeUnit;

publicclassNoBlockQueue{
privatestaticConcurrentLinkedQueue<Integer>concurrentLinkedQueue=newConcurrentLinkedQueue<Integer>();

publicstaticvoidmain(String[]args){
ExecutorServiceexecutorService=Executors.newFixedThreadPool(2);

executorService.submit(newProducer("producer1"));
executorService.submit(newProducer("producer2"));
executorService.submit(newProducer("producer3"));
executorService.submit(newConsumer("consumer1"));
executorService.submit(newConsumer("consumer2"));
executorService.submit(newConsumer("consumer3"));

}

staticclassProducerimplementsRunnable{
privateStringname;

publicProducer(Stringname){
this.name=name;
}

publicvoidrun(){
for(inti=1;i<10;++i){
System.out.println(name+"startproducer"+i);
concurrentLinkedQueue.add(i);
try{
Thread.sleep(20);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
//System.out.println(name+"endproducer"+i);
}
}
}

staticclassConsumerimplementsRunnable{
privateStringname;

publicConsumer(Stringname){
this.name=name;
}
publicvoidrun(){
for(inti=1;i<10;++i){
try{

System.out.println(name+"Consumer"+concurrentLinkedQueue.poll());

}catch(Exceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
//System.out.println();
//System.out.println(name+"endConsumer"+i);
}
}
}
}


在并发编程中,一般推荐使用阻塞队列,这样实现可以尽量地避免程序出现意外的错误。阻塞队列使用最经典的场景就是socket客户端数据的读取和解析,读取数据的线程不断将数据放入队列,然后解析线程不断从队列取数据解析。还有其他类似的场景,只要符合生产者-消费者模型的都可以使用阻塞队列。

使用非阻塞队列,虽然能即时返回结果(消费结果),但必须自行编码解决返回为空的情况处理(以及消费重试等问题)。

另外他们都是线程安全的,不用考虑线程同步问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: