Java多線程②——多線程知識梳理

多線程文章目錄

多線程的常用實現/常用的線程池

 

根據阿里對使用線程池的規範:可以參考Executors的實現,按照業務實現自己的線程池。

注意線程池等資源還是要想着關閉。

初始化時,來一個任務新建一個一個線程;直到核心線程數滿,再往隊列裏面放任務;如果隊列也滿了就繼續新建線程到最大線程數量;如果最大線程數滿就使用拒絕策略;Executor的默認拒絕策略是AbortPolicy;Spring線程池默認是同步執行。

public static void main(String[] args) {
    int threadCount = 10;
    // 依次設置核心線程數、最大線程數、(大於核心數部分)空閒存活時間、存活時間單位、隊列
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    //固定大小線程池、
    ExecutorService executorService = Executors.newFixedThreadPool(10);
    //無界線程池
    Executors.newCachedThreadPool();
    //單個線程池  只有一個線程池的固定大小線程池FIFO
    Executors.newSingleThreadExecutor();

    /** ---------------- 其他線程池--------------**/
    //用於任務調度的線程池    
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
    //工作竊取線程池
    ExecutorService executorServiceFk = Executors.newWorkStealingPool();
}

 

拒絕策略

  • CallerRunsPolicy 同步執行(調用當前線程池的所在的線程去執行被拒絕的任務)
  • AbortPolicy 拋出個RejectedExecutionException異常,不執行此任務
  • DiscardPolicy 直接拋棄任務
  • DiscardOldestPolicy 會拋棄最舊的任務
  • 自定義拒絕策略

線程池常用方法

execute(Runnable)  //無返回值
submit(Runnable/Callable) // 有返回值

shutdown:平滑的關閉ExecutorService,不可以提交新的task,已經提交的將繼續執行(包含提交正在執行和提交未執行)
          當所有已提交任務執行完畢,線程池即被關閉。
          提交使用拒絕策略處理。
awaitTermination:當等待超過設定時間時,會監測ExecutorService是否已經關閉,
         若關閉則返回true,否則返回false。一般情況下會和shutdown方法組合使用 
         接收timeout和unit兩個參數,用於設定超時時間及單位。
shutdownNow :
(1)線程池的狀態立刻變成STOP狀態,此時不能再往線程池中添加新的任務。
(2)終止等待執行的線程,並返回它們的列表;
(3)試圖停止所有正在執行的線程,試圖終止的方法是調用Thread.interrupt(),但是大家知道,如果線程中沒有sleep 、wait、Condition、定時鎖等應用, interrupt()方法是無法中斷當前的線程的。所以,ShutdownNow()並不代表線程池就一定立即就能退出,它可能必須要等待所有正在執行的任務都執行完成了才能退出。

線程池數量設置

cpu密集型:核心線程數 = cpu(處理器)數量

Io密集型:核心線程數 = cpu(處理器)數量 * 2

其他線程池

線程池主要分兩類,ThreadPoolExecutor和ForkJoinPool;使用較多的是ThreadPoolExecutor,也可以通過Executors來創建需要的線程池;

線程池解決了兩個問題: 一是在執行大量的異步任務時,因爲線程池減少了任務開始前的準備工作,如頻繁創建線程,啓動線程等工作,提升了性能表現;二是提供了一種綁定資源和管理資源的途徑,可以進行一些基礎的統計分析,比如已經完成的任務數量等。

 

任務調度線程池

ScheduledExecutorService使用示例

@Slf4j
public class ScheduledThreadPoolDemo implements Runnable{

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //兩種創建方式,都是一個代碼
        //ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(10);
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);

        //使用ScheduledThreadPool來延遲執行Callable或者Runnable,返回一個對象可用來取消或者查詢任務執行情況
        //ScheduledFuture future = scheduledExecutorService.schedule(Callable<V> callable, long delay, TimeUnit unit);
//        Callable c1 = new CallableDemo("A"); RunnableDemo r1 = new RunnableDemo();
//        ScheduledFuture future = scheduledExecutorService.schedule(c1, 3, TimeUnit.SECONDS);
//        future.isDone();

         //循環執行任務,在等待initialDelay.unit後執行,然後每隔period.unit後實行;同樣返回ScheduledFuture對象;
        //如果此任務的任何一個執行要花費比其週期更長的時間,則將推遲(一定時間)後續執行,但不會同時執行。週期爲4秒,執行耗時3秒,每次開始打印間隔一秒;
        //如果任務的任何一個執行遇到異常,則後續執行都會被取消。否則,只能通過執行程序的取消或終止方法來終止該任務。
        //scheduledExecutorService.scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
        ScheduledThreadPoolDemo scheduledThreadPoolDemo = new ScheduledThreadPoolDemo();
        //scheduledExecutorService.scheduleAtFixedRate(scheduledThreadPoolDemo, 5, 4, TimeUnit.SECONDS);

        //和上一個方法差不多,但是會等上一個任務結束,然後再等待delay時間去執行
        //scheduledExecutorService.scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
        scheduledExecutorService.scheduleWithFixedDelay(scheduledThreadPoolDemo, 5, 4, TimeUnit.SECONDS);
    }

    @Override
    public void run() {
        log.info("----執行方法開始---");
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("----執行方法結束---");
    }
}

工作竊取線程池 ForkJoinPool

ForkJoin併發框架:Fork=分解 + Join=合併

ForkJoinPool:ForkJoin線程池,實現了ExecutorService接口和工作竊取算法,用於線程調度與管理。

ForkJoinTask:ForkJoin任務,提供了fork()方法和join()方法。通常不直接使用,而是使用以下子類: RecursiveAction:無返回值的任務,通常用於只fork不join的情形。RecursiveTask:有返回值的任務,通常用於fork+join的情形。

 

根據ForkJoinTask的兩種類型,可以將ForkJoin併發框架劃分爲兩種用法:

only fork:遞歸劃分子任務,分別執行,但是並不需要合併各自的執行結果。

fork+join:遞歸劃分子任務,分別執行,然後遞歸合併計算結果。

參考轉載文章:https://blog.csdn.net/hanchao5272/article/details/79982095

public class TestForkJoinPool {

    public static void main(String[] args) {
        MyTask mt = new MyTask(1);

        // Executors和new ForkJoinPool實現是一樣的,默認情況下Runtime.getRuntime().availableProcessors()得到線程數
        //ExecutorService executorServiceFk = Executors.newWorkStealingPool();
        ForkJoinPool forkJoinPool = new ForkJoinPool(10);
        Future<Integer> result = forkJoinPool.submit(mt);
        try {
            System.out.println(result.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        forkJoinPool.shutdown();
    }

    public static class MyTask extends RecursiveTask<Integer> {
        int i;
        public MyTask(int i) {
            this.i = i;
        }
        @Override
        protected Integer compute() {
            if (i >= 100) {
                return i * i;
            }

            MyTask newTask2 = new MyTask(i + 1);
            newTask2.fork();
            return i*i + newTask2.join();
        }
    }
}

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章