1. 什麼是阻塞隊列
對於共享數據,線程A想讀取的隊列中沒有數據時,處於阻塞狀態。直接線程B將數據寫入共享數據,線程被喚醒。
2.參考如下博客的線程狀態轉換圖和狀態說明,自己也做個收藏(來自:https://my.oschina.net/mingdongcheng/blog/139263)
線程間的狀態轉換:
1. 新建(new):新創建了一個線程對象。
2. 可運行(runnable):線程對象創建後,其他線程(比如main線程)調用了該對象的start()方法。該狀態的線程位於可運行線程池中,等待被線程調度選中,獲取cpu 的使用權 。
3. 運行(running):可運行狀態(runnable)的線程獲得了cpu 時間片(timeslice) ,執行程序代碼。
4. 阻塞(block):阻塞狀態是指線程因爲某種原因放棄了cpu 使用權,也即讓出了cpu timeslice,暫時停止運行。直到線程進入可運行(runnable)狀態,纔有機會再次獲得cpu timeslice 轉到運行(running)狀態。阻塞的情況分三種:
(一). 等待阻塞:運行(running)的線程執行o.wait()方法,JVM會把該線程放入等待隊列(waitting queue)中。
(二). 同步阻塞:運行(running)的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入鎖池(lock pool)中。
(三). 其他阻塞:運行(running)的線程執行Thread.sleep(long ms)或t.join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入可運行(runnable)狀態。
5. 死亡(dead):線程run()、main() 方法執行結束,或者因異常退出了run()方法,則該線程結束生命週期。死亡的線程不可再次復生。
(來自:https://my.oschina.net/mingdongcheng/blog/139263)
3. 阻塞隊列代碼實踐
1)阻塞隊列不停的有數據寫入,發送線程不停從隊列讀取數據,直到標誌位置爲true。
package com.sean.test;
import java.util.concurrent.ArrayBlockingQueue;
public class SendMessageProcess implements Runnable
{
private static ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(9);
public static ArrayBlockingQueue getQueue()
{
return queue;
}
public static void setQueue(ArrayBlockingQueue queue)
{
SendMessageProcess.queue = queue;
}
public boolean isExit()
{
return exit;
}
public void setExit(boolean exit)
{
this.exit = exit;
}
private boolean exit = false;
@Override
public void run()
{
while (!exit)
{
System.out.println("begin SendMessageProcess run().and the exit is:" + exit) ;
String s = null;
try
{
s = queue.take();
System.out.println("end SendMessageProcess run()." + s);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
public void quit() throws InterruptedException
{
queue.put("end the process");
exit = true;
}
}
2)主線程往隊列裏寫入數據,新起一個定時任務,過3s時間將標誌位置爲true。爲了保證阻塞線程喚醒啓動,在標誌位置位的時候往隊列新寫入一個字符數據。
否則線程一直處於阻塞狀態,線程掛死。
package com.sean.test;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class SeanTest
{
private static ScheduledExecutorService service = null;
public static void main(String[] args) throws InterruptedException
{
SendMessageProcess process = new SendMessageProcess();
SendMessageProcess.getQueue().put("test1");
SendMessageProcess.getQueue().put("test2");
service = Executors.newSingleThreadScheduledExecutor();
service.schedule(new newTask(process), 3, TimeUnit.SECONDS);
process.run();
}
public static class newTask implements Runnable
{
SendMessageProcess process = null;
public newTask(SendMessageProcess process)
{
this.process = process;
}
@Override
public void run()
{
System.out.println("Begin newTask run()");
try
{
process.quit();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("End newTask run().exit:" + process.isExit());
service.shutdown();
}
}
}
4. 阻塞隊列在java中適合使用生產者、消費者場景。爲了喚醒消費者,需要生產者往隊列寫入數據。