先分享個有趣的故事給大家,是女大學生被男老師追求的事情,故事的結局不是你們想的那種老牛喫嫩草,喫不到就用強的那種,男的40,女的19,我個人感覺男的比較虛僞的認真了,這是我的個人看法,也可以說他愛的太深了,這個反正我是不太信,女的有點優柔寡斷,明明不喜歡說出來就好,以怕掛科爲理由,一直和老師瞎扯正義和大道理,現在的老師還不至於無緣無故就掛你科。故事到這了,不說了,反正戀愛的請珍惜,分手的祝你分手快樂祝你找個更好的,單身的對自己好點(愛護自己的雙手)。
目錄
- 常用阻塞場景
- 定義
- 核心方法
- java中阻塞隊列種類
- 實現原理(源碼查看)
- 使用場景
- 總結
1.常用的阻塞場景
- 隊列中的生產者和消費者
生產者:是往隊列裏添加元素的線程
消費者:是從隊列裏拿元素的線程
阻塞隊列就是容器,存放元素(別矇蔽好吧,元素就是object) - 阻塞場景
1)當隊列中沒有數據的情況下,消費者端的所有線程被掛起(也就是阻塞了),直到有數據存放入隊列
2)當隊列中填滿數據的情況下,生產者端的所有線程被掛起,直到隊列中有空的位置,線程被自動喚醒
2.定義
符合1.2中兩種場景的隊列,稱爲阻塞隊列
3.核心方法
放入數據的方法:
- offer(anObject):表示將anObject放入到隊列BlockingQueue中,BlockingQueue中可以容納就返回true,否則返回false。(本方法是不會阻塞當前執行方法的線程,反回false,有了解嗎?)
- offer(E o,long timeout,TimeUnit unit):可以設定等待時間。如果在指定時間內還不能往隊列中加入BlockingQuere,則返回false。
- put(anObjiect):和offer是一樣的,但是失敗它不會返回false,會直接阻塞當前方法的線程。
獲取數據的方法:
- poll(long timeout,TimeUnit unit):從BlockingQueue中取出隊首的對象。在指定時間內,隊列一旦有數據可取,則立即返回隊列中的數據,否則超過時間就返回失敗。
- take():取走BlockingQueue裏排在首位的對象。若BlockingQueue爲空,則阻斷進入等待狀態,直到BlockingQueue有新的數據被加入。
- drainTo():一次性從BlockingQueue獲取所有可用的數據對象(還可以指定獲取數據的個數)。通過該方法,可以提升獲取數據的效率;無需多次分批加鎖或者釋放鎖。
當然不單單就這6個方法,你可以點看一下,我就不一一介紹了。
4.java中阻塞隊列種類
- ArrayBlockingQueue:由數組結構組成的有界阻塞隊列
- LinkedBlockingQueue:由鏈表結構組成的有界組賽隊列
- PriorityBlockingQueue:支持優先級排序的無界組賽隊列
- DelayQueue:使用優先級排序的無界阻塞隊列
- SynchronousQueue:不存儲元素的組賽隊列
- LinkedTransferQueue:由鏈表結構組成的無界組賽隊列
- LinkedBlockingDeque:由鏈表結構組成的雙向阻塞隊列
特點:
1.通常情況下,用前兩種就足夠了。
2.PriorityBllockingQueue不能保證同優先級元素的順序。
3.DelayQueue指定元素到期的時間,只有在元素到期時才能從隊列中取走。
4.SynchronousQueue嚴格來說它並不是容器。由於隊列沒有容量,因此不能調用peek操作(返回隊列頭元素)
5.LinkedTransferQueue實現了一個重要接口TransferQueue該接口有5個方法,其中有3個比較重要的方法,一個是transfer(E e)如果沒有消費者等待接受數據,會把元素插入到隊列尾部,隊列進入阻塞狀態,直到有消費者取走該元素,tryTransfer(E e)看名字就知道不會阻塞線程,當然也可以給它傳入時間,在指定的時間內元素未被消費者獲取則返回false。
6.LinkedBlockingDeque是雙向的隊列,多線程同時入隊時減少了一半的競爭。其中的First單詞結尾的方法表示插入、獲取或者移除雙端隊列的第一個元素;以Last單純結尾的方法,表示插入、獲取或者移除雙端隊列的最後一個元素。
5.實現原理(源碼查看)
老規矩,打開我們的Source Insight 4.0,我以ArrayBlockingQueue爲例,搜索看一下這個類定義了那些變量
我們維護的是一個Object的數組,takeIndex和putIndex分別表示隊首元素和隊尾元素的下標,count表示隊列元素的個數,lock則是一個可重入鎖,notEmpty和notFull是等待條件。在看看關鍵方法put
可以看出,先獲取鎖,在判斷當前元素個數是否等於數組長度,如果相等,則調用notFull.await()進行等待。當此線程被其他線程喚醒時,通過enqueue(e)方法插入元素,最後解鎖。在看看enqueue(e)方法
插入成功後,就通過notEmpty.signal()喚醒正在等待取元素的線程。再來看看take方法。
這個應該差不多懂了吧,和put方法類似,如果可以取,就通過dequeue方法取得元素,我們在看看dequeue方法得源碼。
和enqueue方法類似,在獲取元素後,通過等待條件notFull的signal方法喚醒正在等待插入元素的線程。
6.使用場景
第一種使用Object.wait()、Object.notify()和非阻塞隊列實現生產者-消費者模式
package com.example.syt.myapplication;
import java.util.PriorityQueue;
/**
* Created by syt on 2019/4/15.
*/
public class Test {
private int queueSize=10;
private PriorityQueue<Integer> queue=new PriorityQueue<Integer>(queueSize);
public static void main(String[] args) {
Test test=new Test();
Producer producer=test.new Producer();
Consumer consumer=test.new Consumer();
producer.start();
consumer.start();
}
class Consumer extends Thread{
@Override
public void run() {
while (true){
synchronized (queue){
while (queue.size()==0){
try{
System.out.println("隊列空,等待數據");
queue.wait();
}catch (InterruptedException e){
e.printStackTrace();
queue.notify();
}
}
//每次移走隊首元素
queue.poll();
queue.notify();
}
}
}
}
class Producer extends Thread{
@Override
public void run() {
while (true){
synchronized (queue){
while (queue.size()==queueSize){
System.out.println("隊列滿,等待有餘空間");
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
queue.notify();
}
}
//每次插入一個元素
queue.offer(1);
queue.notify();
}
}
}
}
}
在看看我們使用阻塞隊列去實現生產者-消費者模式
package com.example.syt.myapplication;
import java.util.concurrent.LinkedBlockingDeque;
/**
* Created by syt on 2019/4/15.
*/
public class Test {
private int queueSize=10;
private LinkedBlockingDeque<Integer> queue=new LinkedBlockingDeque<Integer>(queueSize);
public static void main(String[] args) {
Test test=new Test();
Producer producer=test.new Producer();
Consumer consumer=test.new Consumer();
producer.start();
consumer.start();
}
class Consumer extends Thread{
@Override
public void run() {
while (true){
try {
queue.take();
System.out.println("取隊首");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer extends Thread{
@Override
public void run() {
try {
queue.put(1);
System.out.println("往隊列加一個");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
總結
阻塞隊列的特點:隊空時消費阻塞,隊滿時生產阻塞。多線程消費數據起到了加速消費的作用,使得生產的數據不會在隊裏積壓過多,而生產的數據也不會丟失處理了。