1. Fork-Join
什麼是分而治之?
規模爲N的問題,N<閾值,直接解決,N>閾值,將N分解爲K個小規模子問題,子問題互相對立,與原問題形式相同,將子問題的解合併得到原問題的解。分而治之思想應用:在快速排序、歸併排序、動態規劃等等。
工作密取(workStealing):線程A完成了工作之後可以被調度去幫線程B完成task,爲了避免競爭獲取任務,從相反的方向獲取任務。
Fork/Join使用的標準範式:
使用:
1.創建RecursiveTask、RecursiveAction的實現類對象myTask
2.ForkJoinPool pool = new ForkJoinPool();
3.調用
pool.invoke(myTask); // 同步調用
pool.execute(myTask); // 異步異步
繼承RecursiveTask類,重寫compute方法
帶返回結果值
案例:統計整形數組中所有元素的和
繼承RecursiveAction類,重寫compute方法
無返回值
案例:遍歷文件夾尋找指定類型文件
public class FindDirsFiles extends RecursiveAction{
private File path;//當前任務需要搜尋的目錄
public FindDirsFiles(File path) {
this.path = path;
}
@Override
protected void compute() {
//任務列表
List<FindDirsFiles> subTasks = new ArrayList<>();
File[] files = path.listFiles();
if(files!=null) {
for(File file:files) {
//不是文件則放進任務列表裏
if(file.isDirectory()) {
subTasks.add(new FindDirsFiles(file));
}else {
//遇到文件,檢查
if(file.getAbsolutePath().endsWith("txt")) {
System.out.println("文件:"+file.getAbsolutePath());
}
}
}
if(!subTasks.isEmpty()) {
//把任務列表丟進去同步執行
for(FindDirsFiles subTask:invokeAll(subTasks)) {
subTask.join();//等待子任務執行完成
}
}
}
}
public static void main(String [] args){
try {
// 用一個 ForkJoinPool 實例調度總任務
ForkJoinPool pool = new ForkJoinPool();
FindDirsFiles task = new FindDirsFiles(new File("D:/"));
pool.execute(task);//異步調用
System.out.println("Task is Running......");
Thread.sleep(1);
int otherWork = 0;
for(int i=0;i<100;i++){
otherWork = otherWork+i;
}
System.out.println("Main Thread done sth......,otherWork="+otherWork);
task.join();//阻塞的方法
System.out.println("Task end");
} catch (Exception e) {
e.printStackTrace();
}
}
}
2. CountDownLatch
作用:是一組線程等待其他的線程完成工作以後在執行,加強版join
await()
用來等待,countDown()
負責計數器的減一
案例實現:主線程 和 業務線程 等待 初始化線程 完成後繼續自己的工作
public class UseCountDownLatch {
static CountDownLatch latch = new CountDownLatch(6);
//初始化線程(後面主線程會new四個實例出來)
private static class InitThread implements Runnable{
@Override
public void run() {
System.out.println("Thread_"+Thread.currentThread().getId()
+" ready init work......");
latch.countDown();//初始化線程完成工作了,countDown方法只扣減一次;
for(int i =0;i<2;i++) {
System.out.println("Thread_"+Thread.currentThread().getId()
+" ........continue do its work");
}
}
}
//業務線程(等待準備完畢後執行)
private static class BusiThread implements Runnable{
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i =0;i<3;i++) {
System.out.println("BusiThread_"+Thread.currentThread().getId()
+" do business-----");
}
}
}
public static void main(String[] args) throws InterruptedException {
//單獨的初始化線程,初始化分爲2步,需要扣減兩次
new Thread(new Runnable() {
@Override
public void run() {
SleepTools.ms(1);
System.out.println("Thread_"+Thread.currentThread().getId()
+" ready init work step 1st......");
latch.countDown();//每完成一步初始化工作,扣減一次
System.out.println("begin step 2nd.......");
SleepTools.ms(1);
System.out.println("Thread_"+Thread.currentThread().getId()
+" ready init work step 2nd......");
latch.countDown();//每完成一步初始化工作,扣減一次
}
}).start();
new Thread(new BusiThread()).start();
for(int i=0;i<=3;i++){
Thread thread = new Thread(new InitThread());
thread.start();
}
latch.await();
System.out.println("Main do ites work........");
}
}
SleepTools 代碼:
public class SleepTools {
/**
* 按秒休眠
* @param seconds 秒數
*/
public static final void second(int seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
}
}
/**
* 按毫秒數休眠
* @param seconds 毫秒數
*/
public static final void ms(int seconds) {
try {
TimeUnit.MILLISECONDS.sleep(seconds);
} catch (InterruptedException e) {
}
}
}
3.CyclicBarrier
CyclicBarrier(int parties)
讓一組線程達到某個屏障,被阻塞,一直到組內最後一個線程達到屏障時,屏障開放,所有被阻塞的線程會繼續運行。
CyclicBarrier(int parties, Runnable barrierAction)
,屏障開放,barrierAction定義的任務會執行。
//創建
CyclicBarrier barrier = new CyclicBarrier(5);
//等待
barrier.await();
/**
* 線程執行到await()方法處會阻塞,當有五個線程都執行到await(),
* 它們會一起繼續執行後面的代碼。
**/
CountDownLatch和CyclicBarrier辨析
1、countdownlatch放行由第三者控制,CyclicBarrier放行由一組線程本身控制
2、countdownlatch放行條件>=線程數,CyclicBarrier放行條件=線程數
舉個簡單的栗子:
CountDownLatch:小明和小紅和小華一起出去自駕遊,
//等兩個人-小明、小紅 latch.await() //等着 )
CountDownLatch latch = new CountDownLatch(2) ;
小華啓動了車子,等着大家搬好東西上車,小明上車後說我準備好了,
latch.countDown();
小紅上車後說我準備好了,
latch.countDown();
(latch值爲0了)就可以出發咯。
CyclicBarrier:小明和小紅和小華一起去旅遊,相約在火車站見面,,先到的等着,三人到齊一起出發。
4. Semaphore
控制同時訪問某個特定資源的線程數量,常用於流量控制
Semaphore sm = new Semaphore(10) ;//大小爲10
sm.acquire();//拿一個 -1
sm.release();//釋放一個 +1
4. Exchange
兩個線程間的數據交換, 因爲只能適用於兩個線程間的交換,使用較少。
示例:
public class UseExchange {
private static final Exchanger<Set<String>> exchange
= new Exchanger<Set<String>>();
public static void main(String[] args) {
//第一個線程
new Thread(new Runnable() {
@Override
public void run() {
Set<String> setA = new HashSet<String>();//存放數據的容器
try {
//添加數據
setA.add("A");
setA = exchange.exchange(setA);//交換set
//處理交換後的數據
for (String s : setA){
System.out.println("我是線程A,我的值爲:"+ s);
}
} catch (InterruptedException e) {
}
}
}).start();
//第二個線程
new Thread(new Runnable() {
@Override
public void run() {
Set<String> setB = new HashSet<String>();//存放數據的容器
try {
//添加數據
setB.add("B");
setB = exchange.exchange(setB);//交換set
//處理交換後的數據
for (String s : setB){
System.out.println("我是線程B,我的值爲:"+ s);
}
} catch (InterruptedException e) {
}
}
}).start();
}
}
5. Callable、Future和FutureTask
isDone()
,結束,正常還是異常結束,或者自己取消,返回true;
isCancelled()
任務完成前被取消,返回true;
cancel(boolean)
:
1、 任務還沒開始,返回false
2、 任務已經啓動,cancel(true),中斷正在運行的任務,中斷成功,返回true,cancel(false),不會去中斷已經運行的任務
3、 任務已經結束,返回false
包含圖片和文字的文檔的處理:圖片(雲上),可以用future去取圖片,主線程繼續解析文字。
案例:使用子線程計算求和後返回給主線程結果,並測試中斷方法:
public class UseFuture {
/*實現Callable接口,允許有返回值*/
private static class UseCallable implements Callable<Integer>{
private int sum;
@Override
public Integer call() throws Exception {
System.out.println("Callable子線程開始計算");
Thread.sleep(2000);//這個睡眠會響應中斷,拋出異常讓線程停止,否則沒有處理中斷,線程不會管別人是否讓他中斷
for(int i=0;i<5000;i++) {
sum = sum+i;
}
System.out.println("Callable子線程計算完成,結果="+sum);
return sum;
}
}
public static void main(String[] args)
throws InterruptedException, ExecutionException {
UseCallable useCallable = new UseCallable();
FutureTask<Integer> futureTask = new FutureTask<Integer>(useCallable);
new Thread(futureTask).start();
Random r = new Random();
SleepTools.second(1);
if(r.nextBoolean()) {//隨機決定是獲得結果還是終止任務
System.out.println("Get UseCallable result = "+futureTask.get());
}else {
System.out.println("中斷計算");
futureTask.cancel(true);
}
}
}