Callable介面和JUC輔助類
Callable介面:
回顧:
建立執行緒的四種方式:
- 繼承Thread
- 實現runnable介面
- 實現callable介面
- 使用執行緒池
之前的文章:多執行緒程式設計1-定義理解與三種實現方式
Runnable和Callable介面的差異:
- Runnable無返回值,Callable有返回值
- Runnable不拋異常,Callable拋異常
- 實現名稱不同,Runnable是run方法,Callable是call方法
class MyThread1 implements Runnable{
@Override
public void run() {
}
}
class MyThread2 implements Callable{
@Override
public Integer call() throws Exception {
return 200;
}
}
Runnable 介面實現類FutureTask
FutureTask構造可以傳遞callable
這是類的繼承結構:
別名:可取消的非同步,簡單的理解是當主執行緒中存在耗時高的任務時,可以單開一個子執行緒處理,主執行緒處理耗時少的任務,最終匯合在一起。
需要注意的是,使用FutureTask當得到第一次結果後,第二次獲取時直接返回結果,也可以說所有的任務只彙總一次。
JUC輔助類:
CountDownLatch(減少計數)
定義:一個同步輔助類,在完成一組正在其他執行緒中執行的操作之前,它允許一個或多個執行緒一直等待。用給定的計數 初始化 CountDownLatch
。由於呼叫了 countDown()
方法,所以在當前計數到達零之前,await
方法會一直受阻塞。之後,會釋放所有等待的執行緒,await
的所有後續呼叫都將立即返回。這種現象只出現一次——計數無法被重置。如果需要重置計數,請考慮使用 CyclicBarrier
。
CountDownLatch
是一個通用同步工具,它有很多用途。將計數 1 初始化的CountDownLatch
用作一個簡單的開/關鎖存器,或入口:在通過呼叫countDown()
的執行緒開啟入口前,所有呼叫await
的執行緒都一直在入口處等待。用 N 初始化的CountDownLatch
可以使一個執行緒在 N 個執行緒完成某項操作之前一直等待,或者使其在某項操作完成 N 次之前一直等待
/**
* 問題,當六個人走出教室,則班長鎖門
*/
public class CountDownLatch07 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(6);
for(int i = 1; i <= 6; i++){
new Thread(() ->{
System.out.println(Thread.currentThread().getName()+" 號同學走出教室");
latch.countDown();
},String.valueOf(i)).start();
}
latch.await();
System.out.println(Thread.currentThread().getName()+"班長鎖門");
}
}
其使用的方法,CountDownLatch latch = new CountDownLatch(6)、latch.countDown()、latch.await();
CyclicBarrier(迴圈柵欄)也可以實現CountDownLatch效果,CyclicBarrier在所有執行緒執行完畢之後是可以重用的。
CyclicBarrier(迴圈柵欄)
原始碼定義:
一種同步輔助工具,它允許一組執行緒全部等待彼此到達公共屏障點。 CyclicBarriers 在涉及固定大小的執行緒組的程式中很有用,這些執行緒必須偶爾相互等待。 屏障被稱為迴圈的,因為它可以在等待執行緒被釋放後重新使用。
簡單的理解,當達到設定的要求後,執行特定的內容,相當於監聽器;
例項程式碼:
public class JucUtils {
private static final Integer NUMBER = 7;
public static void main(String[] args) {
//設定資源要求,達到後所需要執行的任務
CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
System.out.println("資源達到要求!");
});
//對設定的目標前進
for (int i = 1; i <= 7; i++) {
int finalI = i;
new Thread(()->{
try {
System.out.println("資源正在收集:"+ Thread.currentThread().getName());
//等待,當等待的執行緒數量到達設定的值,呼叫執行任務並釋放這些執行緒。
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
感興趣的可以看看原始碼:
-------------------------CyclicBarrier--------------------
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties; //設定臨界值
this.barrierCommand = barrierAction;
}
----------------------------await-----------------------
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
//------------------dowait部分程式碼-------------------
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
int index = --count; //每次等待,所需資源-1;
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
//------nextGeneration----------
private void nextGeneration() {
// signal completion of last generation
trip.signalAll(); //喚醒執行緒
// set up next generation
count = parties;
generation = new Generation();
}
Semaphore(訊號燈)
如果對作業系統有所瞭解的話,該工具類就是訊號量+pv操作的集合,對訊號量的操作只有三種,初始化、p操作、v操作,其中訊號量就是Semaphore初始化的(某種資源的數量),p操作對應的是semaphore.acquire(),訊號量--,v操作對應的semaphore.release(),訊號量++,當Semaphore初始化唯1時,則為互斥資源。
package com.JUC;
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* 類比作業系統的中訊號量PV操作
*/
public class Semaphore07 {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for(int i = 1; i <= 6; i++){
new Thread(()->{
try {
semaphore.acquire(); //加鎖
System.out.println(Thread.currentThread().getName()+" 搶到了車位");
TimeUnit.SECONDS.sleep(new Random().nextInt(5));//隨機時間停車
System.out.println(Thread.currentThread().getName()+"-------離開了車位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
semaphore.release(); //解鎖
}
},String.valueOf(i)).start();
}
}
}