11.可阻塞的队列

来源:互联网 发布:联合程序员开发网 编辑:程序博客网 时间:2024/06/04 19:28
①队列包含固定长度的队列和不固定长度的队列

②什么是可阻塞队列,阻塞队列的作用与实际应用,阻塞队列的实现原理。

阻塞队列是一个在队列基础上又支持了两个附加操作的队列。

2个附加操作:

  • 支持阻塞的插入方法:队列满时,队列会阻塞插入元素的线程,直到队列不满。
  • 支持阻塞的移除方法:队列空时,获取元素的线程会等待队列变为非空。
class BoundedBuffer {   final Lock lock = new ReentrantLock();   final Condition notFull  = lock.newCondition();    final Condition notEmpty = lock.newCondition();    final Object[] items = new Object[100];   int putptr, takeptr, count;   public void put(Object x) throws InterruptedException {     lock.lock();     try {       while (count == items.length)         notFull.await();       items[putptr] = x;       if (++putptr == items.length) putptr = 0;       ++count;       notEmpty.signal();     } finally {       lock.unlock();     }   }   public Object take() throws InterruptedException {     lock.lock();     try {       while (count == 0)         notEmpty.await();       Object x = items[takeptr];       if (++takeptr == items.length) takeptr = 0;       --count;       notFull.signal();       return x;     } finally {       lock.unlock();     }   } }
③阻塞队列与Semaphore有些相似,但也不同,阻塞队列是一方存放数据,另一方释放数据,Semaphore通常则是由同一方设置和释放信号量。
④ArrayBlockingQueue

只有put方法和take方法才具有阻塞功能


⑤用3个空间的队列来演示阻塞队列的功能和效果。

import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;public class BlockingQueueTest {public static void main(String[] args) {final BlockingQueue queue = new ArrayBlockingQueue(3);for(int i=0;i<2;i++){new Thread(){public void run(){while(true){try {Thread.sleep((long)(Math.random()*1000));System.out.println(Thread.currentThread().getName() + "准备放数据!");queue.put(1);System.out.println(Thread.currentThread().getName() + "已经放了数据," + "队列目前有" + queue.size() + "个数据");} catch (InterruptedException e) {e.printStackTrace();}}}}.start();}new Thread(){public void run(){while(true){try {//将此处的睡眠时间分别改为100和1000,观察运行结果Thread.sleep(1000);System.out.println(Thread.currentThread().getName() + "准备取数据!");queue.take();System.out.println(Thread.currentThread().getName() + "已经取走数据," + "队列目前有" + queue.size() + "个数据");} catch (InterruptedException e) {e.printStackTrace();}}}}.start();}}
结果:

Thread-1准备放数据!Thread-1已经放了数据,队列目前有1个数据Thread-0准备放数据!Thread-0已经放了数据,队列目前有2个数据Thread-1准备放数据!Thread-1已经放了数据,队列目前有3个数据Thread-0准备放数据!Thread-1准备放数据!Thread-2准备取数据!Thread-0已经放了数据,队列目前有3个数据Thread-2已经取走数据,队列目前有3个数据Thread-0准备放数据!Thread-2准备取数据!Thread-2已经取走数据,队列目前有2个数据Thread-1已经放了数据,队列目前有3个数据Thread-1准备放数据!Thread-2准备取数据!Thread-0已经放了数据,队列目前有3个数据Thread-2已经取走数据,队列目前有3个数据Thread-0准备放数据!Thread-2准备取数据!Thread-2已经取走数据,队列目前有2个数据Thread-1已经放了数据,队列目前有3个数据Thread-1准备放数据!Thread-2准备取数据!Thread-2已经取走数据,队列目前有2个数据Thread-0已经放了数据,队列目前有3个数据Thread-0准备放数据!Thread-2准备取数据!Thread-2已经取走数据,队列目前有2个数据Thread-1已经放了数据,队列目前有3个数据Thread-1准备放数据!Thread-2准备取数据!。。。。。。。。
⑥用两个具有1个空间的队列来实现同步通知的功能。

import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;/** *第一个线程:A.take()……..B.put() *第二个线程:B.take()……..A.put()  */public class BlockingQueueCommunication {public static void main(String[] args) {final Business business = new Business();new Thread(new Runnable() {public void run() {for (int i = 1; i <= 3; i++) {business.sub(i);}}}).start();for (int i = 1; i <= 3; i++) {business.main(i);}}static class Business {BlockingQueue<Integer> queue1 = new ArrayBlockingQueue<Integer>(1);BlockingQueue<Integer> queue2 = new ArrayBlockingQueue<Integer>(1);//匿名构造函数,比任何构造函数先执行{try {queue2.put(1);} catch (InterruptedException e) {e.printStackTrace();}}/** * 如果在方法头部上加了synchronized,则queue2进不去了,产生死锁 */public void sub(int i) {try {queue1.put(1);} catch (InterruptedException e) {e.printStackTrace();}for (int j = 1; j <= 3; j++) {System.out.println("sub thread sequence of " + j + ",loop of " + i);}try {queue2.take();} catch (InterruptedException e) {e.printStackTrace();}}/** * 如果在方法头部上加了synchronized,则queue1进不去了,产生死锁 */public void main(int i) {try {queue2.put(1);} catch (InterruptedException e) {e.printStackTrace();}for (int j = 1; j <= 3; j++) {System.out.println("main thread sequence of " + j + ",loop of " + i);}try {queue1.take();} catch (InterruptedException e) {e.printStackTrace();}}}}
结果:
sub thread sequence of 1,loop of 1sub thread sequence of 2,loop of 1sub thread sequence of 3,loop of 1main thread sequence of 1,loop of 1main thread sequence of 2,loop of 1main thread sequence of 3,loop of 1sub thread sequence of 1,loop of 2sub thread sequence of 2,loop of 2sub thread sequence of 3,loop of 2main thread sequence of 1,loop of 2main thread sequence of 2,loop of 2main thread sequence of 3,loop of 2sub thread sequence of 1,loop of 3sub thread sequence of 2,loop of 3sub thread sequence of 3,loop of 3main thread sequence of 1,loop of 3main thread sequence of 2,loop of 3main thread sequence of 3,loop of 3
⑦Java 7 提供了7个阻塞队列,如下

a:ArrayBlockingQueue 数组结构组成的有界阻塞队列。

此队列按照先进先出(FIFO)的原则对元素进行排序,但是默认情况下不保证线程公平的访问队列,即如果队列满了,那么被阻塞在外面的线程对队列访问的顺序是不能保证线程公平(即先阻塞,先插入)的。

b:LinkedBlockingQueue一个由链表结构组成的有界阻塞队列

此队列按照先出先进的原则对元素进行排序

c:PriorityBlockingQueue支持优先级的无界阻塞队列

d:DelayQueue支持延时获取元素的无界阻塞队列,即可以指定多久才能从队列中获取当前元素

e:SynchronousQueue不存储元素的阻塞队列,每一个put必须等待一个take操作,否则不能继续添加元素。并且他支持公平访问队列。

f:LinkedTransferQueue由链表结构组成的无界阻塞TransferQueue队列。相对于其他阻塞队列,多了tryTransfer和transfer方法

  • transfer方法,如果当前有消费者正在等待接收元素(take或者待时间限制的poll方法),transfer可以把生产者传入的元素立刻传给消费者。如果没有消费者等待接收元素,则将元素放在队列的tail节点,并等到该元素被消费者消费了才返回。
  • tryTransfer方法,用来试探生产者传入的元素能否直接传给消费者。,如果没有消费者在等待,则返回false。和上述方法的区别是该方法无论消费者是否接收,方法立即返回。而transfer方法是必须等到消费者消费了才返回。

g:LinkedBlockingDeque链表结构的双向阻塞队列,优势在于多线程入队时,减少一半的竞争。

1 0
原创粉丝点击