高并发编程-队列-BlockingQueue-LinkedBlockingQueue
一、LinkedBlockingQueue简介
LinkedBlockingQueue是一个基于链表的阻塞队列,该队列在创建时候,默认大小为Integer.MAX_VALUE,这个数值很大的,所以可以说LinkedBlockingQueue的大小没有限制的,业界有个比较专业的词汇,把它叫做*队列。但是这也带来了一些问题,比如当JVM内存比较小的时候,可能就会出现OOM的情况。所以建议大家在使用的时候根据实际情况设置一个大小。
LinkedBlockingQueue的内部是单向链表,在链表的内部只能next,也就是说查找元素只能从head查起,从tail插入。
LinkedBlockingQueue的采用两把锁的分离技术,实现了出队和入队的锁分离,也就是说LinkedBlockingQueue的读写是分离的,提高了它得并发处理能力
二、LinkedBlockingQueue的特点
队列特点:*阻塞队列,可以指定容量,默认为Integer.MAX_VALUE,先进先出 |
数据结构: 链表结构 |
锁特点:读写锁分离,操作各自的node对象。takeLock 取node节点保证了顺序不会乱,putlock存数据时保证了有序 |
条件阻塞:出队时候,如果队列的count=0,证明队列里面没有元素的,notEmpty条件队列会被阻塞。入队时候,如果队列的count=capacity,证明队列已满,这个时候入队线程需要阻塞,notFull需要阻塞。 |
入队:从tail入队。出队:从head出队 |
三、LinkedBlockingQueue的简单实用
//创建一个有界队列,这个需要指定大写 BlockingQueue<Integer> linkedBlockingQueue = new LinkedBlockingQueue<>(100) //默认指定的Queue为*队列 BlockingQueue<Integer> linkedBlockingQueue = new LinkedBlockingQueue<>(); //建议大家在使用的时候指定队列大小,防止出现OOM
四、LinkedBlockingQueue的源码
数据参数
//指定队列容量大小,不指定默认 Integer.MAX_VALUE private final int capacity; //统计内部元素数量,这里使用了CAS的原子性操作 private final AtomicInteger count = new AtomicInteger(); //定义链表的头部,初始化时 head=null transient Node<E> head; //定义链表的尾部 private transient Node<E> last; //定义take poll使用的锁,也就是消费者使用的锁 private final ReentrantLock takeLock = new ReentrantLock(); //等待队列的条件队列,当队列无元素时,take锁会阻塞在notEmpty条件上,等待其它线程唤醒 private final Condition notEmpty = takeLock.newCondition(); //定义 put, offer,使用的锁,也就是生产者使用的锁 private final ReentrantLock putLock = new ReentrantLock(); //等待入队的条件队列,当队列满了时,put锁会会阻塞在notFull上,等待其它线程唤醒 private final Condition notFull = putLock.newCondition(); //单链表结构 static class Node<E> { E item;//存储元素 Node<E> next;//后续节点,单链表结构 Node(E x) { item = x; } }
构造器
//默认的构造器 public LinkedBlockingQueue() { // 如果没传容量,就使用最大int值初始化其容量 this(Integer.MAX_VALUE); } //指定容量大小的构造器 public LinkedBlockingQueue(int capacity) { //如果指定容量大小小于等于0抛出异常 if (capacity <= 0) throw new IllegalArgumentException(); this.capacity = capacity; //定义初始的head和last节点,初始为空节点 last = head = new Node<E>(null); }