Java常用六種線程池

歡迎大家訪問我的個人博客L_SKH’Blog

一、FixedThreadPool

固定數量線程池

package org.skh.c026;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @Created IntelliJ IDEA.
 * @Author L_SKH
 * @Date 2019/11/18 15:08
 */
public class T05_ThreadPool {
    public static void main(String[] args) throws InterruptedException {
        //固定數量的線程池
        ExecutorService service = Executors.newFixedThreadPool(5);
        //向五個線程提交6個任務
        //因爲是固定數量 所以不會有第六個線程 也所以某個線程會執行兩個任務
        for (int i=0;i<6;i++){
            service.execute(()->{
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());

            });
        }

        System.out.println(service); //打印線程池信息
        /**
         * 狀態: Running,
         * 線程池大小: pool size = 5,
         * 存活的線程數: active threads = 5,
         * 等待的任務的數量: queued tasks = 1,
         * 完成的任務數量: completed tasks = 0
         */
        service.shutdown();  //關閉線程池 但不會立即關閉只會返回一個true 要等待所有任務完成
        System.out.println(service.isTerminated()); //是否停止
        System.out.println(service.isShutdown()); //是否關閉 返回true但沒有真的關閉 只是一個flag
        System.out.println(service);
        TimeUnit.SECONDS.sleep(5);
        System.out.println(service.isTerminated());
        System.out.println(service.isShutdown()); //此時纔是真的關閉了 因爲任務已全部完成
        System.out.println(service);
    }
}

二、CachedPool

與之前的線程池不同 這個是隻有提交了任務時纔會啓動一個線程 一開始是沒有線程的 來一個任務就開啓一個線程 當然前提是線程池裏沒有空閒的並且存活的線程 另外如果一個線程如果在60s內沒有被使用 則會被kill

package org.skh.c026;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @Created IntelliJ IDEA.
 * @Author L_SKH
 * @Date 2019/11/18 20:59
 */
public class T08_CachedPool {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = Executors.newCachedThreadPool();
        System.out.println(service);
        for (int i=0;i<2;i++){
            service.execute(()->{
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());
            });
        }
        System.out.println(service);
        TimeUnit.SECONDS.sleep(80);
        System.out.println(service);
    }
}

三、SingleThreadPool

顧名思義,只有一個線程處理任務的線程,線程池中只有一個線程 所以下面程序中的五個任務只有一個線程執行。所以可以保證任務執行的順序性

package org.skh.c026;

/**
 * @Created IntelliJ IDEA.
 * @Author L_SKH
 * @Date 2019/11/18 21:22
 */

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class T09_SingleThreadPool {

    public static void main(String[] args) {
        ExecutorService service = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i++) {
           int j = i;
            service.execute(() -> {
                System.out.println(j + " " + Thread.currentThread().getName());
            });
        }
    }
}

四、ScheduledPool

ScheduledPool Scheduled: 計劃中的,定時的。執行定時的任務,類似Delay, 可以替代Timer。

package org.skh.c026;

/**
 * @Created IntelliJ IDEA.
 * @Author L_SKH
 * @Date 2019/11/18 21:27
 */

import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class T10_ScheduledPool {

    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(4);
        // 使用固定的頻率執行某個任務
        // 四個參數
        // command: 執行的任務
        // initialDelay: 第一次執行延時多久執行
        // period: 每隔多久執行一次這個任務
        // unit: 時間單位
        service.scheduleAtFixedRate(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        }, 0, 500, TimeUnit.MILLISECONDS);  // 每隔500ms打印一下線程名稱
        //每經過500ms 會在執行一次任務 最大線程數量爲4個 如果sleep的時間大於500會啓用另一個線程來執行
        //當所有的四個線程都在執行任務時 會等待某個線程空閒下來來執行它
    }
}

五、WorkStealingPool

顧名思義,每個線程都有自己維護的隊列,當一個線程處理完自己的隊列後,會去‘偷’別人的任務隊列進行處理。

package org.skh.c026;

/**
 * @Created IntelliJ IDEA.
 * @Author L_SKH
 * @Date 2019/11/18 21:42
 */

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class T11_WorkStealingPool {

    public static void main(String[] args) throws IOException {
        // CPU 核數
        System.out.println(Runtime.getRuntime().availableProcessors());

        // workStealingPool 會自動啓動cpu核數個線程去執行任務
        ExecutorService service = Executors.newWorkStealingPool();
        service.execute(new R(1000));  // 我的cpu核數爲12 啓動13個線程,其中第一個是1s執行完畢,其餘都是2s執行完畢,
        // 有一個任務會進行等待,當第一個執行完畢後,會再次偷取第十三個任務執行
        for (int i = 0; i < Runtime.getRuntime().availableProcessors(); i++) {
            service.execute(new R(2000));
        }

        // 因爲work stealing 是deamon線程,即後臺線程,精靈線程,守護線程
        // 所以當main方法結束時, 此方法雖然還在後臺運行,但是無輸出
        // 可以通過對主線程阻塞解決
        System.in.read();
    }

    static class R implements Runnable {

        int time;

        R(int time) {
            this.time = time;
        }

        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "  " + time);
        }
    }
}

六、ForkJoinPool

將一個任務拆分多個任務執行(可以無限切分),然後將結果合併

package org.skh.c026;

/**
 * @Created IntelliJ IDEA.
 * @Author L_SKH
 * @Date 2019/11/18 21:54
 */

import java.io.IOException;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.RecursiveTask;

/**
 * T12_ForkJoinPool  分而治之
 * Fork: 分叉
 * Join: 合併
 * <p>
 * 將一個任務拆分多個任務執行(可以無限切分),然後將結果合併
 * <p>
 * 比如大量的並行計算, 如下: 求100_0000個數字之和, 使用多線程
 */
public class T12_ForkJoinPool {

    static int[] nums = new int[100_0000];
    static final int MAX_NUM = 5_0000; // 每個線程最多可以運行5萬個數字相加
    static Random random = new Random();

    // 初始化這100_000個數字, 每個數字範圍在100之內
    static {

        for (int i = 0; i < nums.length; i++) {
            nums[i] = random.nextInt(100);
        }
        // 所有數字和, 事先計算:
        System.out.println(Arrays.stream(nums).sum()); // 使用單線程stream api 進行求和
    }

    /**
     * RecursiveAction(AddTask): 遞歸操作 沒有返回值
     * RecursiveTask(AddTask2): 遞歸操作,有返回值
     */

    static class AddTask extends RecursiveAction {

        int start, end;

        AddTask(int start, int end) {
            this.start = start;
            this.end = end;
        }

        @Override
        protected void compute() {

            // 進行計算
            // 如果計算的數的和的範圍 小於 MAX_NUM, 進行計算,否則進行 fork
            if (end - start <= MAX_NUM) {
                long sum = 0;
                for (int i = start; i < end; i++) {
                    sum += nums[i];
                }
                System.out.println("sum = " + sum);
            } else {
                int middle = (end - start) / 2;
                AddTask subTask1 = new AddTask(start, middle);
                AddTask subTask2 = new AddTask(middle, end);
                subTask1.fork();
                subTask2.fork();
            }
        }
    }

    static class AddTask2 extends RecursiveTask<Long> {

        int start, end;

        AddTask2(int start, int end) {
            this.start = start;
            this.end = end;
        }

        @Override
        protected Long compute() {
            // 進行計算
            // 如果計算的數的和的範圍 小於 MAX_NUM, 進行計算,否則進行 fork
            if (end - start <= MAX_NUM) {
                long sum = 0;
                for (int i = start; i < end; i++) {
                    sum += nums[i];
                }
                return sum;
            } else {
                int middle = start + (end - start) / 2; // 注意這裏,如果有問題,會拋出java.lang.NoClassDefFoundError: Could not initialize class java.util.concurrent.locks.AbstractQueuedSynchronizer$Node 異常
                AddTask2 subTask1 = new AddTask2(start, middle);
                AddTask2 subTask2 = new AddTask2(middle, end);
                subTask1.fork();
                subTask2.fork();
                //有返回值
                return subTask1.join() + subTask2.join();
            }
        }
    }

    // 運行
    public static void main(String[] args) throws IOException {
        ForkJoinPool fjp = new ForkJoinPool();

        AddTask2 task = new AddTask2(0, nums.length);

        fjp.execute(task);

        System.out.println(task.join());

        System.in.read();
    }

}


發佈了147 篇原創文章 · 獲贊 62 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章