J.U.C包中提供了一些很是有用的工具類。在合適的場景下使用它們每每可以達到事半功倍的效果。好比Atomic工具類、Exchanger、CountDownLatch、CyclicBarrier、Semaphore這些。java
Atomic工具類可以實現原子操做數據。從數據類型的角度來看,能夠分爲:基本數據類型、數組、引用類型、引用類型屬性的原子更新操做。它的底層原理其實就是對於Unsafe類和volatile的封裝。
像AtomicInteger、AtomicLong等內部源碼都是差很少的。值得注意的是像AtomicBoolean/AtomicChar等的實現原理實際上是內部將boolean、char、byte、等數據類型向上轉型爲了int類型,本質上和AtomicInteger差異不大。數據庫
Exchanger能夠交換線程間的數據,是一個線程間協做工做的工具類。它提供了一個同步點,用於線程間交換數據。Exchanger只能交換2個線程間的數據。理應如此:生活中,不管是交換利益、交換物品,這些行爲都是發生在二者之間。
在實際的場景中,Exchanger十分適用於兩個任務之間有數據上的相互依賴的場景。好比交易系統中的對帳功能。咱們以一個實際例子來看一看Exchanger的使用:數組
public class ExchangerTest {
public static final Exchanger<Integer> transExchanger = new Exchanger<>();
public static void main(String[] args) {
Thread th1 = new Thread(() -> {
// 僞代碼
Integer transA = 1000;
try {
Integer transFromB = transExchanger.exchange(transA);
System.out.println("我是系統A:我係統中統計的交易額爲:" + transA + " 我在交易系統中產生的交易額爲:" + transFromB);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread th2 = new Thread(() -> {
// 僞代碼
Integer transB = 1001;
try {
Integer transFromA = transExchanger.exchange(transB);
System.out.println("我是交易系統:系統A統計出的交易額爲:" + transB + " 系統A實際在我這裏產生的交易額爲:" + transFromA);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
th1.start();
th2.start();
}
}
複製代碼
執行結果以下:網絡
我是交易系統:系統A統計出的交易額爲:1001 系統A實際在我這裏產生的交易額爲:1000
我是系統A:我係統中統計的交易額爲:1000 我在交易系統中產生的交易額爲:1001多線程
CountDownLatch的做用是用於多線程間同步完成任務。值得注意的是它只能使用一次,緣由在於CountDownLatch設置鎖資源以後,只提供了countDown()方法來釋放鎖資源,和await()方法來等待鎖資源釋放完畢。並無重置鎖資源的功能。若是咱們理解AQS源碼的話,那麼讀源碼和對於CountDownLatch的使用就再簡單不過了。併發
public class CountDownLatchTest {
public static final CountDownLatch countDownLatch = new CountDownLatch(2);
public static void main(String[] args) {
Thread th1 = new Thread(() -> {
System.out.println("th1 run.");
countDownLatch.countDown();
});
Thread th2 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("th2 run.");
countDownLatch.countDown();
});
Thread th3 = new Thread(() -> {
System.out.println("th3 waiting until count down 0.");
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("th3 last to run.");
});
th1.start();
th2.start();
th3.start();
}
}
複製代碼
執行結果:框架
th1 run.
th3 waiting until count down 0.
th2 run.
th3 last to run.工具
因爲CountDownLatch只能使用一次,所以它適用於那些一次性任務。好比一些框架的初始化。ui
CyclicBarrier一般被翻譯爲回欄柵。它的功能實際上是相似於CountDownLatch。不一樣之處在於:spa
咱們分析一個例子以下:
public class CyclicBarrierTest {
// 回欄珊,註冊一個額外的線程
public static final CyclicBarrier cyclicBarrier = new CyclicBarrier(4, () -> {
System.out.println("令牌已經爲空了,準備喚醒等待中的線程.");
});
public static void main(String[] args) {
// 建立一個固定大小的線程池
ExecutorService executorService = Executors.newFixedThreadPool(4);
for (int i = 0; i < 4; i++) {
executorService.submit(() -> {
try {
String thName = Thread.currentThread().getName();
System.out.println(thName + " 執行完畢,等待通知...");
cyclicBarrier.await();
System.out.println(thName + " 收到通知.");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
// ignore
}
});
}
executorService.shutdown();
}
}
複製代碼
執行結果以下:
Semaphore翻譯過來是信號量。它的功能主要是控制併發線程數,特別適用於流量的控制,尤爲是那些比較珍貴的公共資源。好比:數據庫的鏈接,IO操做等。
咱們舉個例子以下:
public class SemaphoreTest {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(1);
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "獲取令牌成功");
Thread.sleep(100);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
}
}
}
複製代碼
Semaphore提供了acquire()方法來獲取令牌,使用release來釋放令牌。它也是基於AQS實現的。
本篇文章並無過多的去解讀源碼層面。截止到本篇文章,其實不難發現,若是掌握了Java併發的基本思想和底層基礎知識後,對於應用層面的解讀閱讀大多數實際上是比較簡單的。這也許就是知其因此然的好處罷!