熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

1100行代碼設計一個線程池

2022-06-13   來源: Java高級技術 

  在現代的操作系統中有一個很重要的概念――線程幾乎所有目前流行的操作系統都支持線程線程來源於操作系統中進程的概念進程有自己的虛擬地址空間以及正文段數據段及堆棧而且各自占有不同的系統資源(例如文件環境變量等等)與此不同線程不能單獨存在它依附於進程只能由進程派生如果一個進程派生出了兩個線程那這兩個線程共享此進程的全局變量和代碼段但每個線程各擁有各自的堆棧因此它們擁有各自的局部變量線程在UNIX系統中還被進一步分為用戶級線程(由進程自已來管理)和系統級線程(由操作系統的調度程序來管理)
  
  既然有了進程為什麼還要提出線程的概念呢?因為與創建一個新的進程相比創建一個線程將會耗費小得多的系統資源對於一些小型的應用可能感覺不到這點但對於那些並發進程數特別多的應用使用線程會比使用進程獲得更好的性能從而降低操作系統的負擔另外線程共享創建它的進程的全局變量因此線程間的通訊編程會更將簡單完全可以拋棄傳統的進程間通訊的IPC編程而采用共享全局變量來進行線程間通訊
  
  有了上面這個概念我們下面就進入正題來看一下線程池究竟是怎麼一回事?其實線程池的原理很簡單類似於操作系統中的緩沖區的概念它的流程如下先啟動若干數量的線程並讓這些線程都處於睡眠狀態當客戶端有一個新請求時就會喚醒線程池中的某一個睡眠線程讓它來處理客戶端的這個請求當處理完這個請求後線程又處於睡眠狀態可能你也許會問為什麼要搞得這麼麻煩如果每當客戶端有新的請求時我就創建一個新的線程不就完了?這也許是個不錯的方法因為它能使得你編寫代碼相對容易一些但你卻忽略了一個重要的問題――性能!就拿我所在的單位來說我的單位是一個省級數據大集中的銀行網絡中心高峰期每秒的客戶端請求並發數超過如果為每個客戶端請求創建一個新線程的話那耗費的CPU時間和內存將是驚人的如果采用一個擁有個線程的線程池那將會節約大量的的系統資源使得更多的CPU時間和內存用來處理實際的商業應用而不是頻繁的線程創建與銷毀
  
  既然一切都明白了那我們就開始著手實現一個真正的線程池吧線程編程可以有多種語言來實現例如CC++java等等但不同的操作系統提供不同的線程API接口為了讓你能更明白線程池的原理而避免陷入煩瑣的API調用之中我采用了JAVA語言來實現它由於JAVA語言是一種跨平台的語言因此你不必為使用不同的操作系統而無法編譯運行本程序而苦惱只要你安裝了JDK以上的版本都能正確地編譯運行本程序另外JAVA語言本身就內置了線程對象而且JAVA語言是完全面像對象的因此能夠讓你更清晰地了解線程池的原理如果你注意看一下本文的標題你會發現整個示例程序的代碼只有大約
  
  本示例程序由三個類構成第一個是TestThreadPool類它是一個測試程序用來模擬客戶端的請求當你運行它時系統首先會顯示線程池的初始化信息然後提示你從鍵盤上輸入字符串並按下回車鍵這時你會發現屏幕上顯示信息告訴你某個線程正在處理你的請求如果你快速地輸入一行行字符串那麼你會發現線程池中不斷有線程被喚醒來處理你的請求在本例中我創建了一個擁有個線程的線程池如果線程池中沒有可用線程了系統會提示你相應的警告信息但如果你稍等片刻那你會發現屏幕上會陸陸續續提示有線程進入了睡眠狀態這時你又可以發送新的請求了
  
  第二個類是ThreadPoolManager類顧名思義它是一個用於管理線程池的類它的主要職責是初始化線程池並為客戶端的請求分配不同的線程來進行處理如果線程池滿了它會對你發出警告信息
  
  最後一個類是SimpleThread類它是Thread類的一個子類它才真正對客戶端的請求進行處理SimpleThread在示例程序初始化時都處於睡眠狀態但如果它接受到了ThreadPoolManager類發過來的調度信息則會將自己喚醒並對請求進行處理
  首先我們來看一下TestThreadPool類的源碼
  
  //TestThreadPooljava
   import javaio*;
  
  
   public class TestThreadPool
   {
   public static void main(String[] args)
   {
   try{
   BufferedReader br = new BufferedReader(new InputStreamReader(Systemin));
   String s;
   ThreadPoolManager manager = new ThreadPoolManager();
   while((s = brreadLine()) != null)
   {
   managerprocess(s);
   }
   }catch(IOException e){}
   }
   }
  
  由於此測試程序用到了輸入輸入類因此第行導入了JAVA的基本IO處理包在第行中我們創建了一個名為manager的類它給ThreadPoolManager類的構造函數傳遞了一個值為的參數告訴ThreadPoolManager類我要一個有個線程的池給我創建一個吧!第行至行是一個無限循環它用來等待用戶的鍵入並將鍵入的字符串保存在s變量中並調用ThreadPoolManager類的process方法來將這個請求進行處理
  
  下面我們再進一步跟蹤到ThreadPoolManager類中去以下是它的源代碼
  
  //ThreadPoolManagerjava
   import javautil*;
  
  
   class ThreadPoolManager
   {
  
   private int maxThread;
   public Vector vector;
   public void setMaxThread(int threadCount)
   {
   maxThread = threadCount;
   }
  
   public ThreadPoolManager(int threadCount)
   {
   setMaxThread(threadCount);
   Systemoutprintln(Starting thread pool);
   vector = new Vector();
   for(int i = ; i <= 10; i++)
  20 {
  21 SimpleThread thread = new SimpleThread(i);
  22 vector.addElement(thread);
  23 thread.start();
  24 }
  25 }
  26
  27 public void process(String argument)
  28 {
  29 int i;
  30 for(i = 0; i < vector.size(); i++)
  31 {
  32 SimpleThread currentThread = (SimpleThread)vector.elementAt(i);
  33 if(!currentThread.isRunning())
  34 {
  35 System.out.println("Thread "+ (i+1) +" is processing:" +
  argument);
  36 currentThread.setArgument(argument);
  37 currentThread.setRunning(true);
  38 return;
  39 }
  40 }
  41 if(i == vector.size())
  42 {
  43 System.out.println("pool is full, try in another time.");
  44 }
  45 }
  46 }//end of class ThreadPoolManager
  
  我們先關注一下這個類的構造函數,然後再看它的process()方法。TW.wiNGwIT.CoM第16-24行是它的構造函數,首先它給ThreadPoolManager類的成員變量maxThread賦值,maxThread表示用於控制線程池中最大線程的數量。第18行初始化一個數組vector,它用來存放所有的SimpleThread類,這時候就充分體現了JAVA語言的優越性與藝術性:如果你用C語言的話,至少要寫100行以上的代碼來完成vector的功能,而且C語言數組只能容納類型統一的基本數據類型,無法容納對象。好了,閒話少說,第19-24行的循環完成這樣一個功能:先創建一個新的SimpleThread類,然後將它放入vector中去,最後用thread.start()來啟動這個線程,為什麼要用start()方法來啟動線程呢?因為這是JAVA語言中所規定的,如果你不用的話,那這些線程將永遠得不到激活,從而導致本示例程序根本無法運行。
  下面我們再來看一下process()方法,第30-40行的循環依次從vector數組中選取SimpleThread線程,並檢查它是否處於激活狀態(所謂激活狀態是指此線程是否正在處理客戶端的請求),如果處於激活狀態的話,那繼續查找vector數組的下一項,如果vector數組中所有的線程都處於激活狀態的話,那它會打印出一條信息,提示用戶稍候再試。相反如果找到了一個睡眠線程的話,那第35-38行會對此進行處理,它先告訴客戶端是哪一個線程來處理這個請求,然後將客戶端的請求,即字符串argument轉發給SimpleThread類的setArgument()方法進行處理,並調用SimpleThread類的setRunning()方法來喚醒當前線程,來對客戶端請求進行處理。
  
  可能你還對setRunning()方法是怎樣喚醒線程的有些不明白,那我們現在就進入最後一個類:SimpleThread類,它的源代碼如下:
  
  //SimpleThread.java
  1 class SimpleThread extends Thread
  2 {
  3 private boolean runningFlag;
  4 private String argument;
  5 public boolean isRunning()
  6 {
  7 return runningFlag;
  8 }
  9 public synchronized void setRunning(boolean flag)
  10 {
  11 runningFlag = flag;
  12 if(flag)
  13 this.notify();
  14 }
  15
  16 public String getArgument()
  17 {
  18 return this.argument;
  19 }
  20 public void setArgument(String string)
  21 {
  22 argument = string;
  23 }
  24
  25 public SimpleThread(int threadNumber)
  26 {
  27 runningFlag = false;
  28 System.out.println("thread " + threadNumber + "started.");
  29 }
  30
  31 public synchronized void run()
  32 {
  33 try{
  34 wh
From:http://tw.wingwit.com/Article/program/Java/gj/201311/27304.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.