1. 程式人生 > 其它 >Java JUC併發之執行緒池詳解(重點)

Java JUC併發之執行緒池詳解(重點)

十一、執行緒池(重點)

執行緒池 : 三大方法、7大引數、4種拒絕策略

池化技術

程式執行 => 本質:佔用系統的資源!

如何優化資源的使用? => 池化技術

常用的池:

  • 執行緒池
  • 連線池
  • 記憶體池
  • 物件池
  • ...

注意: 經常建立、銷燬 會造成資源浪費

池化技術: 事先準備好一些資源,如果有人要用,就從這裡拿,用完之後還回來

執行緒池的好處

  • 降低資源的消耗
  • 提高相應的速度
  • 方便管理,防止記憶體洩漏

即 : 執行緒複用、可以控制最大併發數、管理執行緒

執行緒池不允許使用executors去建立,而是通過ThreadPoolExecutor的方式

(讓寫的同學更加明確執行緒池的執行規則,規避資源耗盡的風險)詳細說明見

阿里巴巴Java開發手冊

Executors 返回的執行緒池物件的弊端:

  • FixedThreadPool和SingleThreadPool:

    允許的請求佇列長度為Integer.MAX_VALUE => 21億左右,會堆積大量的請求,導致OOM 【OOM : OutOfMemory,記憶體溢位】

  • CachedThreadPool 和 ScheduledThreadPool :

    允許建立的執行緒數量為Integer.MAX_VALUE,可能會建立大量的執行緒,從而導致OOM.

    三大方法:

    • Executors.newSingleThreadExecutor() => 單一執行緒
    • Executors.newFixedThreadPool(5) => 固定大小的執行緒池
    • Executors.newCachedThreadPool() => 執行緒池大小由CPU處理能力決定 (彈性變化)
    • Executors.newScheduledThreadPool(10) => 建立定長的執行緒池,而且支援定時以及週期性的任務執行
    package com.liu.threadpool;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    // Executors 工具類 四大方法
    public class TestThreadPool {
    
        public static void main(String[] args) {
    
            //ExecutorService threadPool = Executors.newSingleThreadExecutor();  // 單個執行緒
            //ExecutorService threadPool = Executors.newFixedThreadPool(5); // 建立固定數目的執行緒
            //ExecutorService threadPool = Executors.newCachedThreadPool(); // 根據CPU的能力來建立執行緒 遇強則強
            ExecutorService threadPool = Executors.newScheduledThreadPool(10);// 建立定長的執行緒池,而且支援定時以及週期性的任務執行
    
    
            try {
                for (int i = 1; i <= 10000; i++) {
                    threadPool.execute(()->{
                        System.out.println(Thread.currentThread().getName() + " ok");
                });
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                threadPool.shutdown();
            }
        }
    }
    

執行緒池面試題!

四種實現方式 七大引數 四大拒絕策略

原始碼分析:

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, // 21億
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

本質 : ThreadPoolExecutor() !!!

public ThreadPoolExecutor(int corePoolSize, 
                          int maximumPoolSize, 
                          long keepAliveTime, 
                          TimeUnit unit, 
                          BlockingQueue<Runnable> workQueue ) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), 
         defaultHandler);
}
                         
    public ThreadPoolExecutor(int corePoolSize, // 核心執行緒池大小
                              int maximumPoolSize, // 最大核心執行緒池大小
                              long keepAliveTime, // 空閒執行緒的存活時間
                              TimeUnit unit, // 超時等待的時間單位,時間到了會將執行緒釋放,只留下核心執行緒
                              BlockingQueue<Runnable> workQueue, // 阻塞佇列
                              ThreadFactory threadFactory, // 執行緒工廠
                              RejectedExecutionHandler handler // 拒絕策略) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

理解執行緒池的原理: 以銀行辦理業務為例:

手動建立執行緒池

package com.liu.threadpool;

import java.util.concurrent.*;

/**
 * 四種拒絕策略
 * 1. new ThreadPoolExecutor.AbortPolicy()  拒絕策略 銀行滿了,還有客人進來,不處理該業務並丟擲異常
 * 2. new ThreadPoolExecutor.CallerRunsPolicy() // 拒絕策略 哪來的去哪裡! 由main執行緒處理
 * 3. new ThreadPoolExecutor.DiscardPolicy() // 拒絕策略 佇列滿了 不會丟擲異常! 直接丟掉任務
 * 4. new ThreadPoolExecutor.DiscardOldestPolicy() // 拒絕策略 佇列滿了 嘗試和最早的(執行了最長時間的執行緒)競爭,最後失敗了也不會丟擲異常
 */

public class TestThreadPool02 {
    public static void main(String[] args) {

        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3), // 阻塞佇列
                Executors.defaultThreadFactory(), // 執行緒工廠
                new ThreadPoolExecutor.DiscardOldestPolicy() // 拒絕策略 佇列滿了 嘗試和最早的(執行了最長時間的執行緒)競爭,最後失敗了也不會丟擲異常
                );


        try {
            // 最大承載 :Queue + Max
            for (int i = 1; i <= 9; i++) {
                // 使用了執行緒池之後,使用執行緒池來建立執行緒
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }

    }
}

四種拒絕策略

  • AbortPolicy() 銀行滿了,還有客人進來,不處理該業務並丟擲異常
  • CallerRunsPolicy() 哪來的去哪裡! 由main執行緒 或其他執行緒 處理
  • DiscardPolicy() 佇列滿了 不會丟擲異常! 直接丟掉任務
  • DiscardOldestPolicy() 佇列滿了 嘗試和當前最老的執行緒執行的任務競爭,最後失敗了也不會丟擲異常

小結和拓展

  • 最大執行緒到底該如何設定? 依據什麼來定義?(調優!)

    • CPI 密集型 CPU為多少核,就將最大執行緒池大小定義為多少

      可以保持CPU的效率最高!

       // 獲取CPU的核數
              // CPU密集型、IO密集型
             System.out.println(Runtime.getRuntime().availableProcessors());
      
    • IO 密集型 **判斷程式中十分佔用IO資源的的執行緒數 **一般設定為 2 倍

      程式=> 15個大型任務 IO 十分佔用資源 => 設定為 30

package com.liu.threadpool;

import java.util.concurrent.*;
public class TestThreadPool02 {
    public static void main(String[] args) {

        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors(), // cpu核數 即最大執行緒池大小
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3), // 阻塞佇列
                Executors.defaultThreadFactory(), // 執行緒工廠
                new ThreadPoolExecutor.DiscardOldestPolicy() // 拒絕策略 佇列滿了 嘗試和最早的(執行了最長時間的執行緒)競爭,最後失敗了也不會丟擲異常
                );


        try {
            // 最大承載 :Queue + Max
            for (int i = 1; i <= 9; i++) {
                // 使用了執行緒池之後,使用執行緒池來建立執行緒
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }

    }
}

本文來自部落格園,作者:{夕立君},轉載請註明原文連結:https://www.cnblogs.com/liuzhhhao/p/15016591.html