日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Executor 線程池技術詳解

作者:wu1308156206 更新時間: 2022-07-10 編程語言

線程池3大方法

  1. 3種類型的線程池

    • 單一線程線程池

      newSingleThreadExecutor

      public class ThreadPoolTest {
          public static void main(String[] args) {
              //創建一個只有一個線程的線程池
              ExecutorService executorService = Executors.newSingleThreadExecutor();
      
              try{
                  for(int i = 1;i <= 20;i++){
                      executorService.execute(()->{
                          System.out.println(Thread.currentThread().getName()+" ok");
                      });
                  }
              }
              finally {
                  //線程池在不使用后 必須shutdowm
                  executorService.shutdown();
              }
          }
      }
      

      打印

    在這里插入圖片描述

    單一線程的線程池,都是一個線程在執行。

    • 固定個數線程池

      newFixedThreadPool

      public class ThreadPoolTest {
          public static void main(String[] args) {
              //創建一個固定個數的線程池
              ExecutorService executorService = Executors.newFixedThreadPool(5);
      
              try{
                  for(int i = 1;i <= 20;i++){
                      executorService.execute(()->{
                          System.out.println(Thread.currentThread().getName()+" ok");
                      });
                  }
              }
              finally {
                  //線程池在不使用后 必須shutdowm
                  executorService.shutdown();
              }
          }
      }
      

      打印

在這里插入圖片描述

 固定個數的線程池,永遠只有這幾個線程在執行
  • 可伸縮的線程池

    newCachedThreadPool

    public class ThreadPoolTest {
        public static void main(String[] args) {
            //創建一個可伸縮的線程池
            //根據當前任務數調整線程數量
            ExecutorService executorService = Executors.newCachedThreadPool();
    
            try{
                for(int i = 1;i <= 20;i++){
                    executorService.execute(()->{
                        System.out.println(Thread.currentThread().getName()+" ok");
                    });
                }
            }
            finally {
                //線程池在不使用后 必須shutdowm
                executorService.shutdown();
            }
        }
    }
    

    打印

    在這里插入圖片描述

    可伸縮的線程池,線程數根據任務數變化。

線程池7大參數

  1. 先來看上面3種線程池的構造方法

    //newSingleThreadExecutor
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    
    //newFixedThreadPool
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    
    //newCachedThreadPool
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    
    //上面的線程池創建會調用這個方法
    //使用的默認線程工廠和默認的拒絕策略
    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	//當線程池和阻塞隊列都滿了時的拒絕策略
                             ) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
            null :
        AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
    

    從上面源碼可以看出,

    newSingleThreadExecutor和newFixedThreadPool的線程數量雖然是固定的,但是阻塞隊列用的是new LinkedBlockingQueue()沒有設置大小,如果一直往里面添加任務的話,可能會出現OOM異常。

    newCachedThreadPool使用的阻塞隊列雖然是new SynchronousQueue()同步隊列,但是設置的最大線程數是Integer.MAX_VALUE,如果使用不當會把CPU資源耗盡。

    所以上面3種創建線程池的方法都不安全,可以自定義線程池。

  2. 自定義線程池

    public class ThreadPoolTest {
        public static void main(String[] args) {
            /**
             * 自定義線程池
             * 2個核心線程
             * 最大線程數5個
             * 最大空閑時間3
             * 時間單位秒
             * 阻塞隊列為LinkedBlockingDeque 長度為3
             * 線程工程 Executors.defaultThreadFactory
             * 拒接策略 new ThreadPoolExecutor.AbortPolicy()   拋出異常
             */
            ExecutorService executorService = new ThreadPoolExecutor(
                    2,
                    5,
                    3,
                    TimeUnit.SECONDS,
                    new LinkedBlockingDeque<Runnable>(3),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.AbortPolicy()
                    );
    
            int taskNum = 2;
    
            try{
                for(int i = 1;i <= taskNum;i++){
                    for(int i = 1;i <= taskNum;i++){
                        final int tp = i;
                        executorService.execute(()->{
                            System.out.println(Thread.currentThread().getName()+" ok,task" + tp);
                        });
                    }
                }
            }
            finally {
                //線程池在不使用后 必須shutdowm
                executorService.shutdown();
            }
        }
    }
    

    下面通過修改任務數 taskNum 的值來查看執行結果

    • taskNum=2時

      當前任務數和核心線程數一致,2個核心線程執行,阻塞隊列中沒有等待的任務

    在這里插入圖片描述

    • taskNum=5時

      當前任務數超過了核心線程數,但是等待的任務數沒有超過阻塞隊列的長度,這時兩個線程執行,3個任務在隊列中等待,最終還是由兩個核心線程執行任務。

在這里插入圖片描述

  • taskNum=8時

    當前任務數超過了核心線程數和等待隊列的長度的和,這時線程池會再創建3個線程,此時線程數量到達最大線程數5。這時5個線程都執行。

![在這里插入圖片描述](https://img-blog.csdnimg.cn/b72ca842fc1a418886b43995c7a96587.png#pic_center)
  • taskNum>8時

    當前的任務數已經超過了線程池所能容納的最大并發任務數(最大線程數+等待隊列長度),按照線程池創建時指定的拒絕策略,任務只執行到8,后面拋出異常。

在這里插入圖片描述

四種拒絕策略

  1. new ThreadPoolExecutor.AbortPolicy()

    拋出異常

  2. new ThreadPoolExecutor.CallerRunsPolicy()

    由添加線程執行,task9被main線程執行

在這里插入圖片描述

  1. new ThreadPoolExecutor.DiscardPolicy()

    丟掉任務,不拋出異常,任務只執行到8

在這里插入圖片描述

  1. new ThreadPoolExecutor.DiscardOldestPolicy()

    嘗試競爭,不拋出異常

線程池最大值設置問題

分兩種情況

  1. CPU密集型

    這類任務的一個特點是非常占用cpu。所以在設置最大線程數時可以設置為CPU的核心數,這樣即可以保證效率,又不會浪費資源。

    如果設置低了,并發處理任務的效率低。如果設置高了,線程上下文切換的開銷會影響到處理效率。

    可以使用下列語句獲取cpu核心數

    Runtime.getRuntime().availableProcessors()
    
  2. IO密集型

    這里任務的特點是,cpu在處理任務的過程中需要等待IO操作,比如讀寫文件,網絡IO等。

    線程的上下文切換已經對任務處理效率的影響很小了。

    這時可以把最大線程數設置得比cpu核心線程數大一些,比如兩倍。

原文鏈接:https://blog.csdn.net/wu1308156206/article/details/125688787

欄目分類
最近更新