JavaEE進階知識學習----多執行緒JUC高階知識-5-執行緒池-Callable-執行緒排程

weixin_33850890發表於2018-02-25

11.執行緒池

先看一個簡單的例項

public class TestThreadPool {
    public static void main(String[] args) {
        ThreadPoolDemo td = new ThreadPoolDemo();
        new Thread(td).start();
        new Thread(td).start();
        new Thread(td).start();
    }
}
class ThreadPoolDemo implements Runnable{
    private int i = 0;
    @Override
    public void run() {
        while (i<= 100) {
            System.out.println(Thread.currentThread().getName());
        }
        
    }
}

上面我們就是建立和銷燬了三個執行緒例項,這樣不斷的建立和銷燬,就會消耗資源,於是就出現了執行緒池,和資料庫連線池一樣,就是有許多的執行緒已經建立好了,我們直接使用就好。
執行緒池:提供了一個執行緒佇列,佇列中儲存著所有等待狀態的執行緒,避免了建立與銷燬額外的開銷,提高了響應的效能。

執行緒池的體系結構

java.util.concurrent.Executor:負責執行緒的使用與排程的根介面


10417188-632c373979f622d7.png
image

一個 ExecutorService,它使用可能的幾個池執行緒之一執行每個提交的任務,通常使用 Executors 工廠方法配置。

執行緒池可以解決兩個不同問題:由於減少了每個任務呼叫的開銷,它們通常可以在執行大量非同步任務時提供增強的效能,並且還可以提供繫結和管理資源(包括執行任務集時使用的執行緒)的方法。每個 ThreadPoolExecutor 還維護著一些基本的統計資料,如完成的任務數。

工具類Executor

  1. ExecutorService newFixedThreadPool(int nThreads) 建立一個可重用固定執行緒數的執行緒池,以共享的無界佇列方式來執行這些執行緒。
  2. ExecutorService newCachedThreadPool() 建立一個可根據需要建立新執行緒的執行緒池
  3. static ExecutorService newSingleThreadExecutor() 建立一個使用單個 worker 執行緒的 Executor
  4. static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 建立一個執行緒池,它可安排在給定延遲後執行命令或者定期地執行。

執行緒池的使用方法如下:

public class TestThreadPool {
    public static void main(String[] args) {
        // 1.建立執行緒池
        ExecutorService pool = Executors.newFixedThreadPool(5);//建立了5個執行緒的執行緒池
        ThreadPoolDemo td = new ThreadPoolDemo();
        // 2.為執行緒池中的執行緒分配任務
        for (int i = 0; i < 10; i++) {
            pool.submit(td);
        }
        
        // 3.關閉執行緒池
        pool.shutdown();
    }
}
class ThreadPoolDemo implements Runnable{
    private int i = 0;
    @Override
    public void run() {
        while (i<= 5) {
            System.out.println(Thread.currentThread().getName()+":"+i++);
        }
        
    }
}

使用Callable建立執行緒的方式

public class TestThreadPool {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 1.建立執行緒池
        ExecutorService pool = Executors.newFixedThreadPool(5);//建立了5個執行緒的執行緒池
        List<Future<Integer>> list = new ArrayList<Future<Integer>>();
        for (int i = 0; i < 10; i++) {
            Future<Integer> future = pool.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int sum = 0;
                    for (int i = 0; i < 100; i++) {
                        sum +=i;
                    }
                    return sum;
                }
            });
            list.add(future);
        }
        // 3.關閉執行緒池
        pool.shutdown();
        for (Future<Integer> future: list) {
            System.out.println(future.get());
        }
    }
}

12.執行緒排程

線上程池中提到了一個ScheduledExecutorService這一個類就是用來實現執行緒的排程的。

public class TestScheduledThread {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);// 建立五個執行緒的執行緒池
        for (int i = 0; i < 5; i++) {
            ScheduledFuture<Integer> result = pool.schedule(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int num = new Random().nextInt(100);//產生隨機數
                    System.out.println(Thread.currentThread().getName()+":"+num);
                    return num;
                }
            }, 3, TimeUnit.SECONDS);
            System.out.println(result.get());
        }
        pool.shutdown();
        
    }
}

13.Fork/Join框架

Fork/Join框架就是在必要的情況下,將一個大任務,進行拆分(fork)成若干個小任務(拆到不可再拆為止,即臨界值),再將一個個的小任務運算的結果進行join彙總,這裡要說明的是拆分和合並也需要時間,例如求100的累加和,可以使用for迴圈直接累加,也可以使用Fork/Join,但是執行時間反而是for迴圈要快的多,如果要計算100000000就是Fork/Join要快的多,所以包括臨界值的設定都要不停的測試得出,下面給出一個例項吧。

public class TestForkJoinPool {
    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinSum(0L, 1000000L);
        long sum = pool.invoke(task);
        System.out.println(sum);
    }

}
class ForkJoinSum extends RecursiveTask<Long>{

    private static final long serialVersionUID = 1L;
    
    private long start;
    private long end;
    private static final long THURSHOLD = 10000L;// 臨界值
    
    public ForkJoinSum(long start,long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        long length = end -start;
        if(length <= THURSHOLD){
            long sum = 0L;
            for(long i = start; i<= end;i++){
                sum +=i;
            }
            return sum;
        }else{
            long middle = (end+start) / 2;
            ForkJoinSum left = new ForkJoinSum(start, middle);//遞迴操作
            left.fork();// 進行拆分,同時壓入執行緒佇列
            
            ForkJoinSum right = new ForkJoinSum(middle+1, end);
            right.fork();
            return left.join()+right.join();
        }
    }
}

相關文章