JUC之Callable接口回顧和JUC輔助類

Callable接口和JUC輔助類

Callable接口:

回顧:

創建線程的四種方式:

  1. 繼承Thread
  2. 實現runnable接口
  3. 實現callable接口
  4. 使用線程池

之前的文章:多線程編程1-定義理解與三種實現方式

Runnable和Callable接口的差異:

  1. Runnable無返回值,Callable有返回值
  2. Runnable不拋異常,Callable拋異常
  3. 實現名稱不同,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();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章