1.概述
LinkedBlockingQueue 隊(duì)列是一個(gè)單鏈表結(jié)構(gòu)、阻塞的隊(duì)列注益。他的存(put碴巾、offer)取(take丑搔、poll厦瓢、peek提揍、remove)方法都使用了ReentrantLock進(jìn)行加鎖操作,所以他是線程安全的
private final ReentrantLock takeLock = new ReentrantLock();
/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();
/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();
/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();
2.構(gòu)造方法
LinkedBlockingQueue 有三個(gè)構(gòu)造方法 LinkedBlockingQueue()
,LinkedBlockingQueue(int capacity)
,LinkedBlockingQueue(int capacity)
//默認(rèn)設(shè)置的容量為Integer.MAX_VALUE
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
//可以自行指定容量大小
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
//可以傳入一個(gè)容器如(ArrayList旷痕、ArraySet等碳锈;注意Map不行,Map沒(méi)有實(shí)現(xiàn)Collection接口)
public LinkedBlockingQueue(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
final ReentrantLock putLock = this.putLock;
//加鎖
putLock.lock(); // Never contended, but necessary for visibility
try {
int n = 0;
for (E e : c) {
if (e == null)
throw new NullPointerException();
if (n == capacity)
throw new IllegalStateException("Queue full");
//存入到隊(duì)列中
enqueue(new Node<E>(e));
++n;
}
count.set(n);
} finally {
//釋放鎖
putLock.unlock();
}
}
3.存取數(shù)據(jù)方法
3.1 存數(shù)據(jù)的方法
- put() 阻塞欺抗,無(wú)返回值售碳,在隊(duì)尾插入數(shù)據(jù),如果隊(duì)列滿了就會(huì)一直等绞呈,直到空間可用
- offer() 非阻塞贸人, 有返回值,在隊(duì)尾插入數(shù)據(jù)佃声,如果隊(duì)列滿了就直接返回false
- add() 非阻塞艺智,有返回值,內(nèi)部調(diào)用的offer圾亏,但是如果添加失敗就拋異常(如果需要返回值推薦使用offer)
3.2取數(shù)據(jù)的方法
- take() 阻塞十拣,從隊(duì)列頭部獲取元素,并刪除志鹃。當(dāng)隊(duì)列為空時(shí)阻塞夭问,直到有數(shù)據(jù)存進(jìn)來(lái)
- poll() 非阻塞,從隊(duì)列頭部獲取元素曹铃,并刪除缰趋。當(dāng)隊(duì)列為空時(shí)返回null
- peek() 非阻塞,從隊(duì)列頭部獲取元素陕见,不刪除秘血。當(dāng)隊(duì)列為空時(shí)返回null
3.3 移除元素
- remove() 移除隊(duì)列中某個(gè)元素
源碼
簡(jiǎn)單的看兩個(gè)源碼put()
和poll()
其他的原理都是一樣,可自行查看
put()方法,阻塞评甜,無(wú)返回值
public void put(E e) throws InterruptedException {
//如果元素為null拋出異常灰粮,可見(jiàn)LinkedBlockingQueue是不可以存null的
if (e == null) throw new NullPointerException();
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
//加鎖
putLock.lockInterruptibly();
try {
//判斷隊(duì)列是否已滿,滿了就一直等
while (count.get() == capacity) {
notFull.await();
}
//加入到隊(duì)列忍坷,加到隊(duì)尾
enqueue(node);
//數(shù)量+1
c = count.getAndIncrement();
//喚醒等待的線程谋竖,不好解釋可以看一下signal()注釋就明白了
if (c + 1 < capacity)
notFull.signal();
} finally {
//釋放鎖
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
poll() 彈出頂部元素
public E poll() {
final AtomicInteger count = this.count;
//如果隊(duì)列沒(méi)有數(shù)據(jù),則返回null
if (count.get() == 0)
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
//加鎖
takeLock.lock();
try {
if (count.get() > 0) {
//獲取隊(duì)列中第一個(gè)元素承匣,這個(gè)方法里面有點(diǎn)意思(利用垃圾回收算法讓對(duì)象回收,可達(dá)性算法)
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
}
} finally {
//釋放鎖
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
上面兩個(gè)方法一個(gè)放入一個(gè)取出,其他的方法基本都差不多
接下來(lái)看一下這個(gè)方法dequeue()
,這個(gè)方法就是獲取到隊(duì)列的第一個(gè)元素
private E dequeue() {
// assert takeLock.isHeldByCurrentThread();
// assert head.item == null;
Node<E> h = head;
Node<E> first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
return x;
}
上面有這么一行代碼h.next = h; // help GC
當(dāng)時(shí)沒(méi)看明白這個(gè)注釋,后來(lái)才明白這是利用垃圾回收算法中可達(dá)性算法來(lái)對(duì)這個(gè)對(duì)象進(jìn)行回收的.
完!!!!!!