多線程與高併發(七):詳解線程池 - 自定義線程池,JDK自帶線程池,ForkJoin,源碼解析等

Executor 接口關係

在這裏插入圖片描述
在這裏插入圖片描述在這裏插入圖片描述

Callable:類似於Runnable,但是可以有返回值

Future:存儲將來執行的結果。Callable被執行完之後的結果,被封裝到Future裏面。

Future 示例:

/**
 * 認識Callable,對Runnable進行了擴展
 * 對Callable的調用,可以有返回值
 */
package com.mashibing.juc.c_026_01_ThreadPool;

import java.util.concurrent.*;

public class T03_Callable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<String> c = new Callable() {
            @Override
            public String call() throws Exception {
                return "Hello Callable";
            }
        };

        ExecutorService service = Executors.newCachedThreadPool();
        Future<String> future = service.submit(c); //異步

        System.out.println(future.get());//阻塞
        service.shutdown();
    }
}

FutureTask:更加靈活,是Runnable和Future的結合,既是一個Runnable,又可以存結果

FutureTask示例:

/**
 * 認識FutureTask
 */
package com.mashibing.juc.c_026_01_ThreadPool;

import java.util.concurrent.*;

public class T06_00_Future {
    public static void main(String[] args) throws InterruptedException, ExecutionException {

        FutureTask<Integer> task = new FutureTask<>(() -> {
            TimeUnit.MILLISECONDS.sleep(500);
            return 1000;
        }); //new Callable () { Integer call();}

        new Thread(task).start();
        System.out.println(task.get()); //阻塞
    }
}

CompletableFuture
可以用來管理多個Future的結果,對各種各樣的結果進行組合處理。你可以去查查它的API~
提供了很多非常好用的接口,十分友好!

示例:假設你能夠提供一個服務,這個服務查詢各大電商網站同一類產品的價格並彙總展示,你用CompletableFuture開啓三個線程來完成這個任務

package com.mashibing.juc.c_026_01_ThreadPool;

import java.io.IOException;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class T06_01_CompletableFuture {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        long start, end;

        /*start = System.currentTimeMillis();

        priceOfTM();
        priceOfTB();
        priceOfJD();

        end = System.currentTimeMillis();
        System.out.println("use serial method call! " + (end - start));*/

        start = System.currentTimeMillis();

        CompletableFuture<Double> futureTM = CompletableFuture.supplyAsync(() -> priceOfTM());
        CompletableFuture<Double> futureTB = CompletableFuture.supplyAsync(() -> priceOfTB());
        CompletableFuture<Double> futureJD = CompletableFuture.supplyAsync(() -> priceOfJD());

        CompletableFuture.allOf(futureTM, futureTB, futureJD).join(); // 提供對於一堆任務的管理:這三個任務全部完成之後,才能繼續向下運行
//        CompletableFuture.anyOf(futureTM, futureTB, futureJD).join();  // 任意一個任務完成,就能繼續向下運行

        CompletableFuture.supplyAsync(() -> priceOfTM())
                .thenApply(String::valueOf)
                .thenApply(str -> "price " + str)
                .thenAccept(System.out::println);


        end = System.currentTimeMillis();
        System.out.println("use completable future! " + (end - start));

        try {
            System.in.read();//因爲裏面全是異步,所以需要阻塞一下,才能正常的等待它們輸出,不然主線程先結束了。。
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static double priceOfTM() {
        delay();
        return 1.00;
    }

    private static double priceOfTB() {
        delay();
        return 2.00;
    }

    private static double priceOfJD() {
        delay();
        return 3.00;
    }

    /*private static double priceOfAmazon() {
        delay();
        throw new RuntimeException("product not exist!");
    }*/

    private static void delay() {
        int time = new Random().nextInt(500);
        try {
            TimeUnit.MILLISECONDS.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("After random %s sleep!\n", time);
    }
}

線程池

ThreadPoolExecutor:我們通常所說的線程池
ForkJoinPoll:先將任務分解,最後再彙總
在這裏插入圖片描述

如何自定義一個線程池

阿里《Java開發手冊》建議自定義線程池,所以我們先來看看如何自定義一個線程池。

在這裏插入圖片描述

線程池
維護兩個集合:

  • 線程集合
  • 任務集合

用的是HashSet
在這裏插入圖片描述

在這裏插入圖片描述
定義一個自定義線程池,最多有7個參數,這個面試經常被使勁問

在這裏插入圖片描述

package com.mashibing.juc.c_026_01_ThreadPool;

import java.io.IOException;
import java.util.concurrent.*;

public class T05_00_HelloThreadPool {

    static class Task implements Runnable {
        private int i;

        public Task(int i) {
            this.i = i;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " Task " + i);
            try {
                System.in.read();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public String toString() {
            return "Task{" +
                    "i=" + i +
                    '}';
        }
    }

    public static void main(String[] args) {
        ThreadPoolExecutor tpe = new ThreadPoolExecutor(2, 4,
                60, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(4),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());

        for (int i = 0; i < 8; i++) {
            tpe.execute(new Task(i));
        }

        System.out.println(tpe.getQueue());

        tpe.execute(new Task(100));

        System.out.println(tpe.getQueue());

        tpe.shutdown();
    }
}

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