程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java 高並發十: JDK8對並發的新支撐詳解

Java 高並發十: JDK8對並發的新支撐詳解

編輯:關於JAVA

Java 高並發十: JDK8對並發的新支撐詳解。本站提示廣大學習愛好者:(Java 高並發十: JDK8對並發的新支撐詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是Java 高並發十: JDK8對並發的新支撐詳解正文


1. LongAdder

和AtomicLong相似的應用方法,然則機能比AtomicLong更好。

LongAdder與AtomicLong都是應用了原子操作來進步機能。然則LongAdder在AtomicLong的基本長進行了熱門分別,熱門分別相似於有鎖操作中的減小鎖粒度,將一個鎖分別成若干個鎖來進步機能。在無鎖中,也能夠用相似的方法來增長CAS的勝利率,從而進步機能。

LongAdder道理圖:

AtomicLong的完成方法是外部有個value 變量,當多線程並發自增,自減時,均經由過程CAS 指令從機械指令級別操作包管並發的原子性。獨一會制約AtomicLong高效的緣由是高並發,高並發意味著CAS的掉敗概率更高, 重試次數更多,越多線程重試,CAS掉敗概率又越高,釀成惡性輪回,AtomicLong效力下降。

而LongAdder將把一個value拆分紅若干cell,把一切cell加起來,就是value。所以對LongAdder停止加減操作,只須要對分歧的cell來操作,分歧的線程對分歧的cell停止CAS操作,CAS的勝利率固然高了(試想一下3+2+1=6,一個線程3+1,另外一個線程2+1,最初是8,LongAdder沒有乘法除法的API)。

可是在並發數不是很高的情形,拆分紅若干的cell,還須要保護cell和乞降,效力不如AtomicLong的完成。LongAdder用了奇妙的方法來處理了這個成績。

初始情形,LongAdder與AtomicLong是雷同的,只要在CAS掉敗時,才會將value拆分紅cell,每掉敗一次,都邑增長cell的數目,如許在低並發時,異樣高效,在高並發時,這類“自順應”的處置方法,到達必定cell數目後,CAS將不會掉敗,效力年夜年夜進步。

LongAdder是一種以空間換時光的戰略。

2. CompletableFuture

完成CompletionStage接口(40余個辦法),年夜多半辦法多半運用在函數式編程中。而且支撐流式挪用

CompletableFuture是Java 8中對Future的加強版

簡略完成:

import java.util.concurrent.CompletableFuture;

public class AskThread implements Runnable {
 CompletableFuture<Integer> re = null;

 public AskThread(CompletableFuture<Integer> re) {
 this.re = re;
 }

 @Override
 public void run() {
 int myRe = 0;
 try {
 myRe = re.get() * re.get();
 } catch (Exception e) {
 }
 System.out.println(myRe);
 }

 public static void main(String[] args) throws InterruptedException {
 final CompletableFuture<Integer> future = new CompletableFuture<Integer>();
 new Thread(new AskThread(future)).start();
 // 模仿長時光的盤算進程
 Thread.sleep(1000);
 // 告訴完成成果
 future.complete(60);
 }
}

Future最使人诟病的就是要期待,要本身去檢討義務能否完成了,在Future中,義務完成的時光是弗成控的。而 CompletableFuture的最年夜改良在於,義務完成的時光也開放了出來。

future.complete(60);

用來設置完成時光。

CompletableFuture的異步履行:

public static Integer calc(Integer para) {
 try {
 // 模仿一個長時光的履行
 Thread.sleep(1000);
 } catch (InterruptedException e) {
 }
 return para * para;
 }

 public static void main(String[] args) throws InterruptedException,
 ExecutionException {
 final CompletableFuture<Integer> future = CompletableFuture
 .supplyAsync(() -> calc(50));
 System.out.println(future.get());
 }
CompletableFuture的流式挪用:

public static Integer calc(Integer para) {
 try {
 // 模仿一個長時光的履行
 Thread.sleep(1000);
 } catch (InterruptedException e) {
 }
 return para * para;
 }

 public static void main(String[] args) throws InterruptedException,
 ExecutionException {
 CompletableFuture<Void> fu = CompletableFuture
 .supplyAsync(() -> calc(50))
 .thenApply((i) -> Integer.toString(i))
 .thenApply((str) -> "\"" + str + "\"")
 .thenAccept(System.out::println);
 fu.get();
 }

組合多個CompletableFuture:

public static Integer calc(Integer para) {
 return para / 2;
 }

 public static void main(String[] args) throws InterruptedException,
 ExecutionException {
 CompletableFuture<Void> fu = CompletableFuture
 .supplyAsync(() -> calc(50))
 .thenCompose(
  (i) -> CompletableFuture.supplyAsync(() -> calc(i)))
 .thenApply((str) -> "\"" + str + "\"")
 .thenAccept(System.out::println);
 fu.get();
 }

這幾個例子更多是著重Java8的一些新特征,這裡就簡略舉下例子來講明特征,就不深究了。
CompletableFuture跟機能上關系不年夜,更多的是為了支撐函數式編程,在功效上的加強。固然開放了完成時光的設置是一年夜亮點。

3. StampedLock

在上一篇中方才提到了鎖分別,而鎖分別的主要的完成就是ReadWriteLock。而StampedLock則是ReadWriteLock的一個改良。StampedLock與ReadWriteLock的差別在於,StampedLock以為讀不該壅塞寫,StampedLock以為當讀寫互斥的時刻,讀應當是重讀,而不是不讓寫線程寫。如許的設計處理了讀多寫少時,應用ReadWriteLock會發生寫線程饑餓景象。

所以StampedLock是一種傾向於寫線程的改良。

StampedLock示例:

import java.util.concurrent.locks.StampedLock;

public class Point {
 private double x, y;
 private final StampedLock sl = new StampedLock();

 void move(double deltaX, double deltaY) { // an exclusively locked method
 long stamp = sl.writeLock();
 try {
 x += deltaX;
 y += deltaY;
 } finally {
 sl.unlockWrite(stamp);
 }
 }

 double distanceFromOrigin() { // A read-only method
 long stamp = sl.tryOptimisticRead();
 double currentX = x, currentY = y;
 if (!sl.validate(stamp)) {
 stamp = sl.readLock();
 try {
 currentX = x;
 currentY = y;
 } finally {
 sl.unlockRead(stamp);
 }
 }
 return Math.sqrt(currentX * currentX + currentY * currentY);
 }
}

上述代碼模仿了寫線程和讀線程, StampedLock依據stamp來檢查能否互斥,寫一次stamp變增長某個值

tryOptimisticRead()

就是方才所說的讀寫不互斥的情形。

每次讀線程要讀時,會先斷定

if (!sl.validate(stamp))

validate中會先檢查能否有寫線程在寫,然後再斷定輸出的值和以後的 stamp能否雷同,即斷定能否讀線程將讀到最新的數據。

假如有寫線程在寫,或許 stamp數值分歧,則前往掉敗。

假如斷定掉敗,固然可以反復的測驗考試去讀,在示例代碼中,並沒有讓其反復測驗考試讀,而采取的是將悲觀鎖退步成通俗的讀鎖去讀,這類情形就是一種消極的讀法。

stamp = sl.readLock();

StampedLock的完成思惟:

CLH自旋鎖:當鎖請求掉敗時,不會立刻將讀線程掛起,在鎖傍邊會保護一個期待線程隊列,一切請求鎖,然則沒有勝利的線程都記載在這個隊列中。每個節點(一個節點代表一個線程),保留一個標志位(locked),用於斷定以後線程能否曾經釋放鎖。當一個線程試圖取得鎖時,獲得以後期待隊列的尾部節點作為其前序節點。並應用相似以下代碼斷定前序節點能否曾經勝利釋放鎖

while (pred.locked) {  
}

這個輪回就是赓續等後面誰人結點釋放鎖,如許的自旋使適合前哨程不會被操作體系掛起,從而進步了機能。
固然不會停止無停止的自旋,會在若干次自旋後掛起線程。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved