Java實現(xiàn)自定義阻塞隊列
今天重溫了下 java 多線程中的 notify()
方法以及 wait()
方法,一時興起,決定通過這倆個方法,實現(xiàn)一個簡易的自定義阻塞隊列。
阻塞隊列是什么,與普通隊列的區(qū)別是什么?
阻塞隊列與普通隊列的區(qū)別在于,當隊列是空的時,從隊列中獲取元素的操作將會被阻塞,或者當隊列是滿時,往隊列里添加元素的操作會被阻塞。試圖從空的阻塞隊列中獲取元素的線程將會被阻塞,直到其他的線程往空的隊列插入新的元素。同樣,試圖往已滿的阻塞隊列中添加新元素的線程同樣也會被阻塞,直到其他的線程使隊列重新變得空閑起來。
1.新建一個 MyQueue.java 類
import java.util.LinkedList; import java.util.concurrent.atomic.AtomicInteger; import com.xiaoleilu.hutool.util.StrUtil; /** * 使用 notify() 和 wait() 實現(xiàn)自定義阻塞隊列 * * @author Yangkai.Shen * @version 1.0 * @date 2017.08.02 at 11:51:14 */ public class MyQueue { // 1. 承載數(shù)據(jù)的容器 private LinkedList<Object> queue = new LinkedList<Object>(); // 2. 計數(shù)器,用于判定邊界 private AtomicInteger count = new AtomicInteger(0); private final int minSize = 0; // 3. 初始化一個對象,用于加鎖 private final Object lock = new Object(); private final int maxSize; public MyQueue(int maxSize) { this.maxSize = maxSize; } /** * 添加一個元素到隊列中,如果隊列元素已滿,則調用此方法的線程被阻塞,直到存在多余空間了,再進行添加 * * @param obj 添加 obj 到隊列尾部 */ public void put(Object obj) { synchronized (lock) { // 1.沒有多余空間,就阻塞線程 while (count.get() == this.maxSize) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 2.添加元素 queue.add(obj); // 3.計數(shù)器累加 count.incrementAndGet(); System.out.println(StrUtil.format("新加入的元素為:{}", obj)); // 4.喚醒其他線程(若本來元素為空,有線程調用 get 方法,那么原本被阻塞的,需要在此時被喚醒) lock.notify(); } } /** * 獲取一個元素,如果隊列元素為空,則調用此方法的線程被阻塞,直到添加新元素了,再進行獲取 * * @return 返回隊列的第一個元素 */ public Object get() { Object ret = null; synchronized (lock) { // 1.沒有元素,就阻塞線程 while (count.get() == this.minSize) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 2.取第一個元素 ret = queue.removeFirst(); // 3.計數(shù)器遞減 count.decrementAndGet(); System.out.println(StrUtil.format("移除的元素為:{}", ret)); // 4.喚醒其他線程(若元素本來已滿,有線程調用 put 方法,那么原本被阻塞的,需要在此時被喚醒) lock.notify(); } return ret; } public int getSize() { return this.count.get(); } }
2.新建一個測試類 MyQueueTest.java,測試類中,我們初始化一個隊列,并將元素填滿,然后啟動一個線程 t1,去插入數(shù)據(jù),中間休眠 2s,再去啟動一個線程 t2 取數(shù)據(jù)。
import com.xiaoleilu.hutool.util.StrUtil; import java.util.concurrent.TimeUnit; public class MyQueueTest { public static void main(String[] args) { final MyQueue queue = new MyQueue(5); queue.put("a"); queue.put("b"); queue.put("c"); queue.put("d"); queue.put("e"); System.out.println(StrUtil.format("當前隊列的長度: {}", queue.getSize())); Thread t1 = new Thread(() -> { queue.put("f"); queue.put("g"); queue.put("h"); }, "t1"); Thread t2 = new Thread(() -> { queue.get(); queue.get(); }); t1.start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); }
3.啟動測試類,查看運行結果。控制臺如果應該出現(xiàn)的效果是,隊列先初始化完成,然后休眠 2s,接下來先取數(shù)據(jù),再插入數(shù)據(jù),則證明阻塞隊列生效。下面是控制臺運行的效果:
- 初始化隊列
- 休眠 2s 后取隊首元素,再插入隊尾元素
- 此時我們會發(fā)現(xiàn),程序還未停止,因為此時隊列已滿,但是線程
t1
還未插入h
元素,因此線程被阻塞著,直至下次隊列有空余空間才會被喚醒。
4.至此,一個自定義阻塞隊列就已經(jīng)實現(xiàn)了。
5.細心的朋友會發(fā)現(xiàn),我打印的 log 里用到了一個 StrUtil.format() 方法,這個和 slf4j 的 log 用法一致,可以使用占位符。這個是用到了一個國產(chǎn)良心工具類,hutool,國產(chǎn)開源,需要大家的支持,覺得好用的話,期望可以去 碼云 或者 github 上給個 Star 吧!
以上就是Java實現(xiàn)自定義阻塞隊列的詳細內容,更多關于Java 自定義阻塞隊列的資料請關注腳本之家其它相關文章!
相關文章
Java實現(xiàn)對兩個List快速去重并排序操作示例
這篇文章主要介紹了Java實現(xiàn)對兩個List快速去重并排序操作,結合實例形式較為詳細的分析了Java針對list的遍歷、去重、排序相關操作技巧與注意事項,需要的朋友可以參考下2018-07-07java 通過聚合查詢實現(xiàn)elasticsearch的group by后的數(shù)量
這篇文章主要介紹了java 通過聚合查詢實現(xiàn)elasticsearch的group by后的數(shù)量,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-12-12Java中Double除保留后小數(shù)位的幾種方法(小結)
這篇文章主要介紹了Java中Double保留后小數(shù)位的幾種方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-07-07IDEA:Git stash 暫存分支修改的實現(xiàn)代碼
這篇文章主要介紹了IDEA:Git stash 暫存分支修改的實現(xiàn)代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03Java使用JavaMail API發(fā)送和接收郵件的代碼示例
JavaMail是Oracle甲骨文開發(fā)的Java郵件類API,支持多種郵件協(xié)議,這里我們就來看一下Java使用JavaMail API發(fā)送和接收郵件的代碼示例2016-06-06