線程池學習

来源:http://www.cnblogs.com/czl362326/archive/2016/07/18/5682532.html
-Advertisement-
Play Games

新建一個線程並啟動,開銷會很大,因為運行線程需要的資源比調用對象方法需要的資源多得多。在很多情況下,線程被用於執行一類任務,而這類任務數量很多,發生的時間分佈不均,如果為每個新任務都啟用一個新線程來執行,則開銷會太大,可以採用一種性能優化技術,就是使用線程池。 將若幹執行任務的線程放在池中,當有任務 ...


  新建一個線程並啟動,開銷會很大,因為運行線程需要的資源比調用對象方法需要的資源多得多。在很多情況下,線程被用於執行一類任務,而這類任務數量很多,發生的時間分佈不均,如果為每個新任務都啟用一個新線程來執行,則開銷會太大,可以採用一種性能優化技術,就是使用線程池。

  將若幹執行任務的線程放在池中,當有任務要執行時,從池中取出一個空閑線程來處理任務,處理完任務後,再講線程對象放入池中。線程池實際上就是一個對象池,只是池中的對象都是線程。

  本文實例將實現一個線程池,可以給線程池分配任務,線程池中的線程自動獲取任務並執行。

  關鍵技術:1.線程組ThreadGroup可以管理多個線程,所以讓線程池繼承ThreadGroup。

       2.無條件關閉線程池時,通過ThreadGroup的interrupt方法中斷池中的所有線程。

       3.有條件關閉線程池時,通過ThreadGroup獲得池中所有活動線程的引用,依次調用Thread的join方法等待活動線程執行完畢。當所有線程都運行結束時,線程池才                        能被關閉。

       4.將任務放在LinkedList中,由於LinkedList不支持同步,所以在添加任務和獲取任務的方法聲明中必須使用Synchronized關鍵字。

實例

package book.thread.pool;
/**
*定義任務的介面類
*/
public interface Task {
  public void perform() throws Exception;
}

package book.thread.pool;

public class MyTask implements Task{
  private int taskID = 0;//任務ID
  public MyTask(int id){
    this.taskID = id;
  }

  @Override
  public void perform() throws Exception {
    System.out.println("MyTask " + taskID + ":start");
    Thread.sleep(1000);
    System.out.println("MyTask " + taskID + ":end");
  }
}

package book.thread.pool;

import java.util.LinkedList;

public class MyThreadPool extends ThreadGroup{
  private boolean isAlive;//標誌線程池是否開啟
  private LinkedList taskQueue;//線程池中的任務隊列
  private int threadID;//線程池中的線程ID
  private static int threadPoolID;//線程池ID
  //創建新的線程池,numThreads是池中的線程數
  public MyThreadPool(int numThreads){
    super("ThreadPool-"+(threadPoolID++));
    //設置該線程池的Daemon屬性為true,表示當該線程池中的所有線程都被銷毀時,該線程池會自動被銷毀
    super.setDaemon(true);
    this.isAlive = true;
    this.taskQueue = new LinkedList();//新建一個任務隊列
    //啟動numThreads個工作線程
    for(int i = 0;i < numThreads; i++){
      new PooledThread().start();
    }
  }
  //添加新任務
  public synchronized void performTask(Task task){
    if(!this.isAlive){
      throw new IllegalStateException();//線程池被關閉,則拋出異常
    }
    if(task != null){
      this.taskQueue.add(task);//將任務放到任務隊列的尾部
      notify();//通知工作線程取任務
    }
  }
  //獲取任務
  protected synchronized Task getTask() throws InterruptedException{
    //如果任務列表為空,而且線程池沒有被關閉,則繼續等待任務
    while(this.taskQueue.size() == 0){
      if(!this.isAlive){
        return null;
      }
      wait();
    }
    //取任務列表的第一個任務
    return (Task)this.taskQueue.removeFirst();
  }
  //關閉線程池,所有線程停止,不再執行任務
  public synchronized void close(){
    if(isAlive){
      this.isAlive = false;
      this.taskQueue.clear();//清除任務
      this.interrupt();//中止線程池中的所有線程
    }
  }
  //關閉線程池,並等待線程池中的所有任務運行完成,但不能接收新任務
  public void join(){
    //通知其他等待線程“該線程池已關閉”的消息
    synchronized(this){
      isAlive = false;
      notifyAll();
    }
  //等待所有線程完成,首先建立一個新的線程組,activeCount方法獲取線程池中活動線程的估計數
  Thread[] threads = new Thread[this.activeCount()];
  //將線程池中的活動線程拷貝到新創建的線程組threads中
  int count = this.enumerate(threads);
  for(int i = 0;i < count; i++){
    try {
      threads[i].join();//等待線程運行結束
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}
//內部類,用於執行任務的工作線程
private class PooledThread extends Thread{
  public PooledThread(){
    //第一個參數為該線程所在的線程組對象,即當前線程池對象
    //第二個參數為線程名字
    super(MyThreadPool.this,"PooledThread-" +(threadID++));
  }
  public void run(){
    //如果該線程沒有被中止
    while(!isInterrupted()){
      //獲取任務
      Task task = null;
    try {
      task = getTask();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    //只要線程池的任務列表不為空,getTask方法就總能得到一個任務
    //若getTask()返回null,則表示線程池中已經沒有任務,而且線程池已經被關閉
    if(task == null){
      return;
    }
    //運行任務,捕捉異常
    try {
      task.perform();
     } catch (Exception e) {
      uncaughtException(this,e);
     }
    }
  }
  }
}

package book.thread.pool;

public class PoolTest {
  public static void main(String[] args) {
    int numThreads = 3;//線程池中的線程數
    MyThreadPool threadPool = new MyThreadPool(numThreads);//生成線程池
    int numTasks = 10;//任務數
    //運行任務
    for(int i = 0;i<numTasks;i++){
      threadPool.performTask(new MyTask(i));
    }
    //關閉線程池並等待所有任務完成
    threadPool.join();
  }
}

輸出結果:

MyTask 0:start
MyTask 1:start
MyTask 2:start
MyTask 0:end
MyTask 3:start
MyTask 1:end
MyTask 4:start
MyTask 2:end
MyTask 5:start
MyTask 3:end
MyTask 6:start
MyTask 4:end
MyTask 7:start
MyTask 5:end
MyTask 8:start
MyTask 6:end
MyTask 9:start
MyTask 7:end
MyTask 8:end
MyTask 9:end

結果分析:MyThreadPool類是線程池的主體類,用於管理一組工作線程。

       1.繼承ThreadGroup,可以使用ThreadGroup提供的方法管理線程池中的線程。

       2.performTask公有同步方法往線程池的任務隊列中添加一個任務。如果線程池已被關閉,即isAlive屬性為false,則不允許添加任務;添加任務後,調用notify方                         法,通知池中的工作線程取任務。

       3.getTask受保護同步方法從線程池的任務隊列中獲取一個任務。之所以聲明為受保護的,是為了限制其他類的對象非法獲取任務。如果任務隊列中沒有任務,則當                        前線程進入等待狀態,如果線程池已被關閉,則直接返回null。

       4.close方法強制關閉線程池。通過ThreadGroup的interrupt方法中斷線程池中所有運行的線程,清空任務隊列,並且isAlive屬性設置為false,表示不接收新任務

       5.join方法有條件的關閉線程池。isAlive屬性置為false,表示線程池不再接收新任務,通過ThreadGroup獲得正在運行的線程,通過Thread的join方法等待他們執                       行完任務後,再關閉線程池。

    PooledThread類是MyThreadPool的內部類,定義了工作線程,處於MyThreadPool線程池中。在run放在中不斷的從線程池的任務隊列中取任務,取到任務後,調用任務的perform方法執行任務。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、表單標簽庫 1.1、簡介 從Spring2.0起就提供了一組全面的自動數據綁定標簽來處理表單元素。生成的標簽相容HTML 4.01與XHTML 1.0。表單標簽庫中包含了可以用在JSP頁面中渲染HTML元素的標簽。表單標記庫包含在spring-webmvc.jar中,庫的描述符稱為spring- ...
  • python之線程、進程和協程 目錄: 引言 一、線程 1.1 普通的多線程 1.2 自定義線程類 1.3 線程鎖 1.3.1 未使用鎖 1.3.2 普通鎖Lock和RLock 1.3.3 信號量(Semaphore) 1.3.4 事件(Event) 1.3.5 條件(condition) 1.3 ...
  • 這是我以前的BS4筆記,交流請聯繫 QQ 328123440 ...
  • 在PHP後端和客戶端數據交互的過程中,JSON數據中有時格式不定,一會兒是數組,一會兒是對象,弄得客戶端開發人員要崩潰的感覺。 因此,前後端相關人員先對PHP的json_encode函數原理有必要的瞭解是最重要的一個環節。 PHP中的array是個萬能的數據結構,並不像其它語言根據需要的場景會定義很 ...
  • 做leetcode的題 We are playing the Guess Game. The game is as follows: I pick a number from 1 to n. You have to guess which number I picked. Every time yo ...
  • 一、數組(一維): 對於聲明變數,int score1 = 81; 等等這些操作,如果我們有四個成績,是不是可以定義變數score 1、2、3、4呢?但是,如果我們要定義400個成績又當如何呢? 這就可以用到數組了,數組可以指定一個長度,然後在數組裡可以存放這個長度範圍的同類型數組,可以將數組理解為 ...
  • JSP的基本語法 一、JSP頁面中的JAVA代碼 二、JSP頁面中的指令 三、JSP頁面中的隱含對象(九大內置對象) 一、JSP頁面中的JAVA代碼 JSP表達式(方便輸出) JSP小腳本(完成相對較長的邏輯運算) JSP聲明(添加屬性或方法) 1、JSP表達式(格式:<%=%>) 註意:表達式結束 ...
  • 一、SpEL:Spring 表達式語言,在使用的時候類似於 EL 表達式,但是需要註意的是,SpEL 使用在 Spring Config 文件中。 二、格式:使用 #{} 作為界定符,所有在大括弧中的字元都將被認為成是 SeEL 三、作用: 1.通過 Bean 的 id 對 Bean 進行引用 2. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...