Java線程池的應用


為什么要用線程池

線程是不是越多好?

  • 線程在Java中是一個對象, 更是操作系統的資源, 線程額創建和銷毀都需要時間,如果 創建時間+銷毀時間>執行任務時間 就很不合算
  • Java對象占用堆內存,操作系統線程占用系統內存, 根據JVM規范,一個線程默認最大棧大小為1M, 這個棧空間是要從操作系統內存中分配的,線程過多會消耗很多的內存
  • 操作系統頻繁切換線程上下文會影響性能

線程池的推出就是為了控制線程數量

線程池原理 - 概念

  • 線程池管理器:用於創建並管理線程池, 包括創建線程池, 銷毀線程池, 添加新任務
  • 工作線程: 線程池中的線程, 在沒有任務時處於等待狀態, 可以循環的執行任務
  • 任務接口: 每個任務必須實現的接口,以供工作線程任務調度的執行, 它主要規定了任務的入口,任務執行完后的收尾工作,任務的執行狀態等
  • 任務隊列: 用於存放沒有處理的任務,提供一種緩沖

img

線程池API - 接口定義和實現類

img

線程池API - 方法定義

ExecutorService

img

ScheduledExecutorService

img

線程池API - Executors工具類

可以自己實例化線程池, 也可以用Executors創建線程池,方法如下:

  • newFixedThreadPool(int nThreads) : 創建一個固定大小,任務隊列無界的的線程池,核心線程數=最大線程數
  • newCachedThreadPool() : 創建一個大小無界的緩沖線程池,它的任務隊列是一個同步隊列,任務加入加入到池中,如果池中有空閑線程, 則用空閑線程執行,如無則創建新線程執行,池中的空閑線程超過60秒, 將被銷毀釋放, 線程數隨任務的多少變化,適用於耗時較小的異步任務, 池的核心線程數=0, 最大線程數=Integer.MAX_VALUE
  • newSingleThreadExecutor() : 只有一個線程來執行無界任務隊列的單一線程池,該線程池確保任務按加入的順序一個一個的依次執行,當唯一的線程因任務異常中止時,將創建一個新的線程來繼續執行后續的任務, 與newFixedThreadPool(1)的區別在於,單一線程池的池大小在newSingleThreadExecutor方法中硬編碼,不能再改變
  • newScheduledThreadPool(int corePoolSize) : 能定時執行任務的線程池,該池的核心線程數由參數指定,最大線程數=Integer.MAX_VALUE

線程池原理 - 任務執行過程

  1. 是否達到核心線程數量? 沒達到,創建一個工作線程來執行任務
  2. 工作隊列是否已滿?沒滿,則將新提交的任務存儲在任務隊列中
  3. 是否達到線程池最大數量?則創建一個新的線程來執行任務
  4. 最后,執行拒絕策略來處理這個任務

img

代碼示例:

package com.neteasy.demo;

import java.util.List;
import java.util.concurrent.*;

/** 線程池的使用 */
public class Demo7 {

  /**
   * 測試: 提交15個執行時間需要3秒的任務,看線程池的狀況
   *
   * @param threadPoolExecutor
   * @throws Exception
   */
  private void testCommon(ThreadPoolExecutor threadPoolExecutor) throws Exception {
    // 測試: 提交15個執行時間需要3秒的任務,看超過大小的2個,對應的處理情況
    for (int i = 0; i < 15; i++) {
      int n = i;
      threadPoolExecutor.submit(
          new Runnable() {
            @Override
            public void run() {
              try {
                System.out.println("開始執行: " + n);
                Thread.sleep(3000L);
                System.out.println("執行結束: " + n);
              } catch (InterruptedException e) {
                e.printStackTrace();
              }
            }
          });
      System.out.println("任務提交成功:" + i);
    }
    // 查看線程數量,查看隊列等待數量
    Thread.sleep(500L);
    System.out.println("當前線程池線程數量為:" + threadPoolExecutor.getPoolSize());
    System.out.println("當前線程池等待的數量為" + threadPoolExecutor.getQueue().size());
    // 等待15秒,查看線程數量和隊列數量(理論上,會被超出核心線程數量的線程自動銷毀)
    Thread.sleep(15000L);
    System.out.println("當前線程池線程數量為:" + threadPoolExecutor.getPoolSize());
    System.out.println("當前線程池等待的數量為:" + threadPoolExecutor.getQueue().size());
  }

  /**
   * 1、線程池信息: 核心線程數量5,最大數量10,無界隊列,超出核心線程數量的線程存活時間:5秒, 指定拒絕策略的
   *
   * @throws Exception
   */
  private void threadPoolExecutorTest1() throws Exception {
    ThreadPoolExecutor threadPoolExecutor =
        new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
    testCommon(threadPoolExecutor);
    // 預計結果:線程池線程數量為:5,超出數量的任務,其他的進入隊列中等待被執行
  }

  /**
   * 2、 線程池信息: 核心線程數量5,最大數量10,隊列大小3,超出核心線程數量的線程存活時間:5秒, 指定拒絕策略的
   *
   * @throws Exception
   */
  private void threadPoolExecutorTest2() throws Exception {
    // 創建一個 核心線程數量為5,最大數量為10,等待隊列最大是3 的線程池,也就是最大容納13個任務。
    // 默認的策略是拋出RejectedExecutionException異常,java.util.concurrent.ThreadPoolExecutor.AbortPolicy
    ThreadPoolExecutor threadPoolExecutor =
        new ThreadPoolExecutor(
            5,
            10,
            5,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<Runnable>(3),
            new RejectedExecutionHandler() {
              @Override
              public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.err.println("有任務被拒絕執行了");
              }
            });
    testCommon(threadPoolExecutor);
    // 預計結果:
    // 1、 5個任務直接分配線程開始執行
    // 2、 3個任務進入等待隊列
    // 3、 隊列不夠用,臨時加開5個線程來執行任務(5秒沒活干就銷毀)
    // 4、 隊列和線程池都滿了,剩下2個任務,沒資源了,被拒絕執行。
    // 5、 任務執行,5秒后,如果無任務可執行,銷毀臨時創建的5個線程
  }

  /**
   * 3、 線程池信息: 核心線程數量5,最大數量5,無界隊列,超出核心線程數量的線程存活時間:5秒
   *
   * @throws Exception
   */
  private void threadPoolExecutorTest3() throws Exception {
    // 和Executors.newFixedThreadPool(int nThreads)一樣的
    ThreadPoolExecutor threadPoolExecutor =
        new ThreadPoolExecutor(
            5, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    testCommon(threadPoolExecutor);
    // 預計結:線程池線程數量為:5,超出數量的任務,其他的進入隊列中等待被執行
  }

  /**
   * 4、 線程池信息: 核心線程數量0,最大數量Integer.MAX_VALUE,SynchronousQueue隊列,超出核心線程數量的線程存活時間:60秒
   *
   * @throws Exception
   */
  private void threadPoolExecutorTest4() throws Exception {

    /**
     * SynchronousQueue,實際上它不是一個真正的隊列,因為它不會為隊列中元素維護存儲空間。 與其他隊列不同的是,它維護一組線程,這些線程在等待着把元素加入或移出隊列。
     * 在使用SynchronousQueue作為工作隊列的前提下,客戶端代碼向線程池提交任務時, 而線程池中又沒有空閑的線程能夠從SynchronousQueue隊列實例中取一個任務,
     * 那么相應的offer方法調用就會失敗(即任務沒有被存入工作隊列)。 此時,ThreadPoolExecutor會新建一個新的工作者線程用於對這個入隊列失敗的任務進行處理
     * (假設此時線程池的大小還未達到其最大線程池大小maximumPoolSize)。
     */
    // 和Executors.newCachedThreadPool()一樣的
    ThreadPoolExecutor threadPoolExecutor =
        new ThreadPoolExecutor(
            0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
    testCommon(threadPoolExecutor);
    // 預計結果:
    // 1、 線程池線程數量為:15,超出數量的任務,其他的進入隊列中等待被執行
    // 2、 所有任務執行結束,60秒后,如果無任務可執行,所有線程全部被銷毀,池的大小恢復為0
    Thread.sleep(60000L);
    System.out.println("60秒后,再看線程池中的數量:" + threadPoolExecutor.getPoolSize());
  }

  /**
   * 5、 定時執行線程池信息:3秒后執行,一次性任務,到點就執行
   * 核心線程數量5,最大數量Integer.MAX_VALUE,DelayedWorkQueue延時隊列,超出核心線程數量的線程存活時間:0秒
   *
   * @throws Exception
   */
  private void threadPoolExecutorTest5() throws Exception {
    // 和Executors.newScheduledThreadPool()一樣的
    ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(5);
    threadPoolExecutor.schedule(
        new Runnable() {
          @Override
          public void run() {
            System.out.println("任務被執行,現在時間:" + System.currentTimeMillis());
          }
        },
        3000,
        TimeUnit.MILLISECONDS);
    System.out.println(
        "定時任務,提交成功,時間是:"
            + System.currentTimeMillis()
            + ", 當前線程池中線程數量:"
            + threadPoolExecutor.getPoolSize());
    // 預計結果:任務在3秒后被執行一次
  }

  /**
   * 6、 定時執行線程池信息:線程固定數量5 ,<br>
   * 核心線程數量5,最大數量Integer.MAX_VALUE,DelayedWorkQueue延時隊列,超出核心線程數量的線程存活時間:0秒
   *
   * @throws Exception
   */
  private void threadPoolExecutorTest6() throws Exception {
    ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(5);
    /**
     * 周期性執行某一個任務,線程池提供了兩種調度方式,這里單獨演示一下。測試場景一樣。 測試場景:提交的任務需要3秒才能執行完畢。看兩種不同調度方式的區別 效果1:
     * 提交后,2秒后開始第一次執行,之后每間隔1秒,固定執行一次(如果發現上次執行還未完畢,則等待完畢,完畢后立刻執行)。
     * 也就是說這個代碼中是,3秒鍾執行一次(計算方式:每次執行三秒,間隔時間1秒,執行結束后馬上開始下一次執行,無需等待)
     */
    threadPoolExecutor.scheduleAtFixedRate(
        new Runnable() {
          @Override
          public void run() {
            try {
              Thread.sleep(3000L);
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
            System.out.println("任務-1 被執行,現在時間:" + System.currentTimeMillis());
          }
        },
        2000,
        1000,
        TimeUnit.MILLISECONDS);

    /**
     * 效果2:提交后,2秒后開始第一次執行,之后每間隔1秒,固定執行一次(如果發現上次執行還未完畢,則等待完畢,等上一次執行完畢后再開始計時,等待1秒)。
     * 也就是說這個代碼鍾的效果看到的是:4秒執行一次。 (計算方式:每次執行3秒,間隔時間1秒,執行完以后再等待1秒,所以是 3+1)
     */
    threadPoolExecutor.scheduleWithFixedDelay(
        new Runnable() {
          @Override
          public void run() {
            try {
              Thread.sleep(3000L);
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
            System.out.println("任務-2 被執行,現在時間:" + System.currentTimeMillis());
          }
        },
        2000,
        1000,
        TimeUnit.MILLISECONDS);
  }

  private ThreadPoolExecutor testShutdownCommon() {
    // 創建一個 核心線程數量為5,最大數量為10,等待隊列最大是3的線程池,也就是最大容納13個任務。
    // 默認的策略是拋出RejectedExecutionException異常,java.util.concurrent.ThreadPoolExecutor.AbortPolicy
    ThreadPoolExecutor threadPoolExecutor =
        new ThreadPoolExecutor(
            5,
            10,
            5,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(3),
            new RejectedExecutionHandler() {
              @Override
              public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.err.println("有任務被拒絕執行了");
              }
            });
    // 測試: 提交15個執行時間需要3秒的任務,看超過大小的2個,對應的處理情況
    for (int i = 0; i < 15; i++) {
      int n = i;
      threadPoolExecutor.submit(
          new Runnable() {
            @Override
            public void run() {
              try {
                System.out.println("開始執行:" + n);
                Thread.sleep(3000L);
                System.err.println("執行結束:" + n);
              } catch (InterruptedException e) {
                System.out.println("異常:" + e.getMessage());
              }
            }
          });
      System.out.println("任務提交成功 :" + i);
    }
    return threadPoolExecutor;
  }

  /**
   * 7、 終止線程:線程池信息: 核心線程數量5,最大數量10,隊列大小3,超出核心線程數量的線程存活時間:5秒, 指定拒絕策略的
   *
   * @throws Exception
   */
  private void threadPoolExecutorTest7() throws Exception {
    ThreadPoolExecutor threadPoolExecutor = testShutdownCommon();
    // 1秒后終止線程池
    Thread.sleep(1000L);
    threadPoolExecutor.shutdown();
    // 再次提交提示失敗
    threadPoolExecutor.submit(
        new Runnable() {
          @Override
          public void run() {
            System.out.println("追加一個任務");
          }
        });
    // 結果分析
    // 1、 10個任務被執行,3個任務進入隊列等待,2個任務被拒絕執行
    // 2、調用shutdown后,不接收新的任務,等待13任務執行結束
    // 3、 追加的任務在線程池關閉后,無法再提交,會被拒絕執行
  }

  /**
   * 8、 立刻終止線程:線程池信息: 核心線程數量5,最大數量10,隊列大小3,超出核心線程數量的線程存活時間:5秒, 指定拒絕策略的
   *
   * @throws Exception
   */
  private void threadPoolExecutorTest8() throws Exception {
    // 創建一個 核心線程數量為5,最大數量為10,等待隊列最大是3 的線程池,也就是最大容納13個任務。
    // 默認的策略是拋出RejectedExecutionException異常,java.util.concurrent.ThreadPoolExecutor.AbortPolicy
    ThreadPoolExecutor threadPoolExecutor = testShutdownCommon();
    // 1秒后終止線程池
    Thread.sleep(1000L);
    List<Runnable> shutdownNow = threadPoolExecutor.shutdownNow();
    // 再次提交提示失敗
    threadPoolExecutor.submit(
        new Runnable() {
          @Override
          public void run() {
            System.out.println("追加一個任務");
          }
        });
    System.out.println("未結束的任務有:" + shutdownNow.size());

    // 結果分析
    // 1、 10個任務被執行,3個任務進入隊列等待,2個任務被拒絕執行
    // 2、調用shutdownnow后,隊列中的3個線程不再執行,10個線程被終止
    // 3、 追加的任務在線程池關閉后,無法再提交,會被拒絕執行
  }

  public static void main(String[] args) throws Exception {
    Demo7 demo7 = new Demo7();
    //    demo7.threadPoolExecutorTest1();
    //    demo7.threadPoolExecutorTest2();
    //    demo7.threadPoolExecutorTest3();
    //    demo7.threadPoolExecutorTest4();
    //    demo7.threadPoolExecutorTest5();
    //    demo7.threadPoolExecutorTest6();
    //    demo7.threadPoolExecutorTest7();
    demo7.threadPoolExecutorTest8();
  }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM