日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java轻松实现无锁队列

發布時間:2023/12/10 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java轻松实现无锁队列 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1、什么是無鎖(Lock-Free)編程

? ? ? ?當談及 Lock-Free 編程時,我們常將其概念與 Mutex(互斥) 或 Lock(鎖) 聯系在一起,描述要在編程中盡量少使用這些鎖結構,降低線程間互相阻塞的機會,以提高應用程序的性能。類同的概念還有?"Lockless" 和 "Non-Blocking" 等。實際上,這樣的描述只涵蓋了 Lock-Free編程的一部分內容。本質上說,Lock-Free 編程僅描述了代碼所表述的性質,而沒有限定或要求代碼該如何編寫。

基本上,如果程序中的某一部分符合下面的條件判定描述,則我們稱這部分程序是符合?Lock-Free的。反過來說,如果某一部分程序不符合下面的條件描述,則稱這部分程序是不符合 Lock-Free 的。

? ? ? ?上面的英文翻譯成中文就是很簡單的:如果你的應用程序是多線程并且它們之間都有訪問共享內存但是訪問時并沒有相互阻塞,那它就是lock-free編程。注意lock-free只是強調了編程概念并沒指定其具體的實現形式,其強調的概念是「線程間訪問共享內存時不會相互阻塞」。那如果沒有lock或者Mutex就一定是lock-free編程了嗎,看下面的代碼片段:

? ? ?

x = 0;while(x == 0){x = 1 - x;}

? ? ? ?假設有線程T1,T2同時調用這段代碼,T1,T2都判斷x == 0,進行到循環。T1先執行 x = 1 - 0,此時 x = 1后 T2 執行 x = 1 - 1。x = 0。T1,T2此時判斷x == 0,結果兩者又進入了循環。。。線程T1,T2相互影響,兩者都陷入了死循環,這種某種意義也算得上是相互阻塞使線程,所以這不算是lock-free編程。

?ok,了解了lock-free編程的相關概念那要怎么實現呢。在開始說無鎖隊列之前,我們需要知道一個很重要的技術就是CAS操作——Compare & Set,或是 Compare & Swap,現在幾乎所有的CPU指令都支持CAS的原子操作,X86下對應的是?CMPXCHG?匯編指令。有了這個原子操作,我們就可以用其來實現各種無鎖(lock free)的數據結構。

這個操作用C語言來描述就是下面這個樣子:意思就是說,看一看內存*reg里的值是不是oldval,如果是的話,則對其賦值newval。

1 2 3 4 5 6 7 int compare_and_swap (int* reg, int oldval, int newval) { ??int old_reg_val = *reg; ??if (old_reg_val == oldval) ?????*reg = newval; ??return old_reg_val; }

用JAVA語言則是:

public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}

  

了解了CAS操作之后實現lock-free數據結構思路是怎樣呢?這里就有篇論文講述了思路:http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.53.8674&rep=rep1&type=pdf。其中里面就提到了如何用數組實現一個lock-free隊列。有興趣的朋友可以參考上面鏈接閱讀里面的第5章節。現在說一下我自己具體的實現思路:

  • 數組隊列是一個循環數組,隊列少用一個元素,當頭等于尾標示隊空,尾加1等于頭標示隊滿。
  • 數組的元素用EMPTY(無數據,標示可以入隊)和FULL(有數據,標示可以出隊)標記指示,數組一開始全部初始化成 EMPTY標示空隊列。
  • EnQue 操作:如果當前隊尾位置為EMPTY,標示線程可以在當前位置入隊,通過CAS原子操作把該位置設置為FULL,避免其它線程操作這個位置,操作完后修改隊尾位置。各個線程競爭新的隊尾位置。如下圖所示:

?


?

下面是貼上具體的代碼:

? ? ??

import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceArray;/*** 用數組實現無鎖有界隊列*/public class LockFreeQueue {private AtomicReferenceArray atomicReferenceArray;//代表為空,沒有元素private static final Integer EMPTY = null;//頭指針,尾指針AtomicInteger head,tail;public LockFreeQueue(int size){atomicReferenceArray = new AtomicReferenceArray(new Integer[size + 1]);head = new AtomicInteger(0);tail = new AtomicInteger(0);}/*** 入隊* @param element* @return*/public boolean add(Integer element){int index = (tail.get() + 1) % atomicReferenceArray.length();if( index == head.get() % atomicReferenceArray.length()){System.out.println("當前隊列已滿,"+ element+"無法入隊!");return false;}while(!atomicReferenceArray.compareAndSet(index,EMPTY,element)){return add(element);}tail.incrementAndGet(); //移動尾指針System.out.println("入隊成功!" + element);return true;}/*** 出隊* @return*/public Integer poll(){if(head.get() == tail.get()){System.out.println("當前隊列為空");return null;}int index = (head.get() + 1) % atomicReferenceArray.length();Integer ele = (Integer) atomicReferenceArray.get(index);if(ele == null){ //有可能其它線程也在出隊return poll();}while(!atomicReferenceArray.compareAndSet(index,ele,EMPTY)){return poll();}head.incrementAndGet();System.out.println("出隊成功!" + ele);return ele;}public void print(){StringBuffer buffer = new StringBuffer("[");for(int i = 0; i < atomicReferenceArray.length() ; i++){if(i == head.get() || atomicReferenceArray.get(i) == null){continue;}buffer.append(atomicReferenceArray.get(i) + ",");}buffer.deleteCharAt(buffer.length() - 1);buffer.append("]");System.out.println("隊列內容:" +buffer.toString());}}

  代碼很簡單,相應的注釋也寫上了,相信大家都應該看得懂~。

? ? ? ?這里說明一下JDK提供的CAS原子操作類都位于?java.util.concurrent.atomic下面。這里用到的是數組我用的是AtomicReferenceArray類,當然你也可以用AtomicIntegerArray。這里用到了兩個原子類的作為指針head,tail,利用mod隊列的長度來實現一個循環數組。

? ? ? ?下面測試我們的代碼:?

import java.util.stream.IntStream;public class LockFreeDemo {public static void main(String[] args) {LockFreeQueue queue = new LockFreeQueue(5);IntStream.rangeClosed(1, 10).parallel().forEach(i -> {if (i % 2 == 0) {queue.add(i);} else {queue.poll();}});queue.print();} }

? ? ? ?這里面用了JDK8的lambda并行流的特性,起了Ncpu線程去并發得入隊和出隊。運行結果如下:

入隊成功!2 當前隊列為空 當前隊列為空 入隊成功!6 當前隊列為空 入隊成功!8 出隊成功!2 入隊成功!10 出隊成功!6 入隊成功!4 隊列內容:[4,8,10]

? ? ? ?因為是并發打印,所以打出來的信息整體是無序的,但是對于同一個元素的操作,我們看到是相對有序的~??

轉載于:https://www.cnblogs.com/linlinismine/p/9263426.html

總結

以上是生活随笔為你收集整理的java轻松实现无锁队列的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。