类图结构
同样首先看一下LinkedBlockingQueue的类图结构,以便从全局对LinkedBlockingQueue有个直观的了解。
由类图可以看到,LinkedBlockingQueue也是使用单向链表实现的,其也有两个Node,分别用来存放首、尾节点,并且还有一个初始值为0的原子变量count,用来记录队列元素个数。
另外还有两个ReentrantLock的实例,分别用来控制元素入队和出队的原子性,其中takeLock用来控制同时只有一个线程可以从队列头获取元素,其他线程必须等待,putLock控制同时只能有一个线程可以获取锁,在队列尾部添加元素,其他线程必须等待。
另外,notEmpty和notFull是条件变量,它们内部都有一个条件队列用来存放进队和出队时被阻塞的线程,其实这是生产者—消费者模型。
-
当调用线程在LinkedBlockingQueue实例上执行take、poll等操作时需要获取到takeLock锁,从而保证同时只有一个线程可以操作链表头节点。
另外由于条件变量notEmpty内部的条件队列的维护使用的是takeLock的锁状态管理机制,所以在调用notEmpty的await和signal方法前调用线程必须先获取到takeLock锁,否则会抛出IllegalMonitorStateException异常。
notEmpty内部则维护着一个条件队列,当线程获取到takeLock锁后调用notEmpty的await方法时,调用线程会被阻塞,然后该线程会被放到notEmpty内部的条件队列进行等待,直到有线程调用了notEmpty的signal方法。
-
在LinkedBlockingQueue实例上执行put、offer等操作时需要获取到putLock锁,从而保证同时只有一个线程可以操作链表尾节点。
同样由于条件变量notFull内部的条件队列的维护使用的是putLock的锁状态管理机制,所以在调用notFull的await和signal方法前调用线程必须先获取到putLock锁,否则会抛出IllegalMonitorStateException异常。
notFull内部则维护着一个条件队列,当线程获取到putLock锁后调用notFull的await方法时,调用线程会被阻塞,然后该线程会被放到notFull内部的条件队列进行等待,直到有线程调用了notFull的signal方法。
默认队列容量为0x7fmf,用户也可以自己指定容量,所以从一定程度上可以说LinkedBlockingQueue是有界阻塞队列。
LinkedBlockingQueue原理介绍
offer操作
向队列尾部插入一个元素,如果队列中有空闲则插入成功后返回true,如果队列已满则丢弃当前元素然后返回false。
如果e元素为null则抛出NullPointerException异常。另外,该方法是非阻塞的。
put操作
向队列尾部插入一个元素,如果队列中有空闲则插入后直接返回,如果队列已满则阻塞当前线程,直到队列有空闲插入成功后返回。
如果在阻塞时被其他线程设置了中断标志,则被阻塞线程会抛出InterruptedException异常而返回。
另外,如果e元素为null则抛出NullPointerException异常。
poll操作
从队列头部获取并移除一个元素,如果队列为空则返回null,该方法是不阻塞的。
peek操作
获取队列头部元素但是不从队列里面移除它,如果队列为空则返回null。该方法是不阻塞的。
take操作
获取当前队列头部元素并从队列里面移除它。
如果队列为空则阻塞当前线程直到队列不为空然后返回元素,如果在阻塞时被其他线程设置了中断标志,则被阻塞线程会抛出InterruptedException异常而返回。
remove操作
删除队列里面指定的元素,有则删除并返回true,没有则返回false。
size操作
获取当前队列元素个数。