搜尋
首頁Javajava教程java 可重啟線程及線程池類別的設計(詳解)

了解JAVA多執行緒程式設計的人都知道,要產生一個執行緒有兩種方法,一是類別直接繼承Thread類別並實作其run()方法;二是類別實作Runnable介面並實作其run()方法,然後新建一個以此類別為建構方法參數的Thread,類似於下列形式: Thread t=new Thread(myRunnable)。而最終使執行緒啟動都是執行Thread類別的start()方法。

在JAVA中,一個執行緒一旦運行完畢,即執行完其run()方法,就不可以重新啟動了。此時這個線程對像也便成了無用對象,等待垃圾回收器的回收。下次想再啟動這個線程時,就必須重新new出一個線程物件再start之。頻繁地創建和銷毀物件不僅影響運行效率,還可能因無用線程物件來不及被回收而產生大量的垃圾內存,在儲存空間和處理速度都相對受限的移動平台上這種影響尤為顯著。那麼,能否重新設計一種線程類,使其能夠被反覆啟動而無需頻繁地創建和銷毀物件呢?

當然可以。下面我就介紹一下這個「可重啟線程」類別的設計。

首先必須明確,如果仍是把想要線程去做的任務直接放在線程的run()方法中,是無論如何無法達成目的的,因為就像上面已經說的,JAVA的線程類別一旦執行完run()方法就無法再啟動了。所以唯一可行的辦法是,把使用者程式要做的run()方法(不妨稱作「使用者流程」)套在執行緒實際的run()方法內部的while循環體內,當使用者過程執行完後使執行緒wait 。當呼叫restart方法重啟執行緒時,實際上就是喚醒等待中的執行緒使之開始下一次while迴圈。大致的想法確定了,下面的程式碼就很好理解了:

public class ReusableThread implements Runnable {
 //线程状态监听者接口
 public interface ThreadStateListener {
  public abstract void onRunOver(ReusableThread thread);//当用户过程执行完毕后调用的方法
 }
  
 public static final byte STATE_READY=0; //线程已准备好,等待开始用户过程
 public static final byte STATE_STARTED=1; //用户过程已启动
 public static final byte STATE_DESTROYED=2; //线程最终销毁
  
 byte mState; //标示可重启线程的当前状态
  
 Thread mThread; //实际的主线程对象
 Runnable mProc; //用户过程的run()方法定义在mProc中
 ThreadStateListener mListener; //状态监听者,可以为null
  
 /** Creates a new instance of ReusableThread */
 public ReusableThread(Runnable proc) {
  mProc = proc;
  mListener = null;
  mThread = new Thread(this);
  mState = STATE_READY;
 }
  
 public byte getState() {return mState;}
  
 public void setStateListener(ThreadStateListener listener) {
  mListener = listener;
 }
  
 /**可以在处于等待状态时调用该方法重设用户过程*/
 public synchronized boolean setProcedure(Runnable proc) {
  if (mState == STATE_READY) {
   mProc = proc;
   return true;
  }
  else
   return false;
 }
  
 /**开始执行用户过程*/
 public synchronized boolean start() {
  if (mState == STATE_READY) {
   mState = STATE_STARTED;
   if (!mThread.isAlive()) mThread.start();
   notify(); //唤醒因用户过程执行结束而进入等待中的主线程
   return true;
  }
  else
   return false;
 }
  
 /**结束整个线程,销毁主线程对象。之后将不可再次启动*/
 public synchronized void destroy() {
  mState = STATE_DESTROYED;
  notify();
  mThread = null;
 }
  
 public void run() {
  while (true) {
   synchronized (this) {
    try {
     while (mState != STATE_STARTED) {
      if (mState == STATE_DESTROYED) return;
      wait();
     }
    } catch(Exception e) {e.printStackTrace();}
   }
    
   if (mProc != null) mProc.run();
   if (mListener != null) mListener.onRunOver(this); //当用户过程结束后,执行监听者的onRunOver方法
    
   synchronized (this) {
    if (mState == STATE_DESTROYED) return;
    mState = STATE_READY;
   }
  }
 }
  
}

程式碼很好懂是不是?但是要解釋為什麼要有一個「狀態監聽者」介面。有時候我們可能想要在使用者流程結束後得到一個及時的通知,好進行另外的處理,這時狀態監聽者的onRunOver方法就有了用處。一個直觀的例子是,在下面要提到的「線程池」類別中,一個可重啟線程執行完一次用戶過程後應當自動回收入池,這時就可以把回收入池的動作放在onRunOver方法中,而它的參數就是該可重啟線程對象,於是就可以把參數所指示的對象回收進線程池。
  

至於執行緒池類,其實就是先前提到的物件池類別的子類,其中的物件全是ReusableThread類別的。另外它實現了ReusableThread.ThreadStateListener接口,以便在用戶過程結束時及時收到通知,執行回收線程的工作:

public class ThreadPool extends ObjectPool implements ReusableThread.ThreadStateListener {
 public static final int DefaultNumThreads = 16; //默认池容量
  
 public ReusableThread getThread() {
  return (ReusableThread)fetch();
 }
  
 public void onRunOver(ReusableThread thread) {
  recycle(thread); //当用户过程结束时,回收线程
 }
  
 private void init(int size) {
  ReusableThread thread;
  //初始化线程池内容
  for (int i=0; i<size; i++) {
   thread = new ReusableThread(null);
   thread.setStateListener(this);
   setElementAt(thread, i);
  }
 }
  
 public ThreadPool(int size) {
  super(size);
  init(size);
 }
  
 public ThreadPool() {
  super(DefaultNumThreads);
  init(DefaultNumThreads);
 }
  
}

當然,還有一些可能需要添加的功能,因為既然只是比普通線程多了一個可重啟的「增強」型線程類,那麼原來Thread類別具有的功能也應該具有,例如線程的sleep()。不過那些比較簡單,這裡就略去了。

下面編寫測試程式。我準備這樣進行:並不用到線程池類,而是對對像池類和可重啟線程類進行聯合測試,該對像池中的對象所屬的類CharEmitter實現了Runnable接口和線程狀態監聽者接口,並且含有一個可重啟線程成員對象,它並不包含在任何線程池對像中,而是獨立使用的。當此執行緒的使用者程序(定義在CharEmitter類別)結束後,onRunOver方法執行回收本CharEmitter物件入池的動作。這樣就同時起到了間接測試執行緒池類別的作用,因為它與物件池的差異也不過是在onRunOver中執行回收動作而已。

還是直接上程式碼說得清楚:

TestThreadPool.java :

/**字符放射器*/
class CharEmitter implements Runnable, ReusableThread.ThreadStateListener {
 char c; //被发射的字符
 boolean[] isEmitting; //标示某字符是否正被发射(直接以字符对应的ASCII码作下标索引)
 
 ReusableThread thread; //可重启线程对象
 
 ObjectPool myHomePool; //为知道应把自己回收到哪里,需要保存一个到自己所在对象池的引用
 
 CharEmitter(ObjectPool container, boolean[] isCharEmitting) {
  isEmitting=isCharEmitting;
  myHomePool=container;
  thread=new ReusableThread(this); //新建可重启线程对象,设其用户过程为CharEmitter类自己定义的
 }
 
 /**开始“发射”字符*/
 public void emit(char ch) {
  //字符被要求只能是&#39;0&#39;到&#39;9&#39;之间的数字字符
  if (ch>=&#39;0&#39; && ch<=&#39;9&#39;) {
   c=ch;
  }
  else c=&#39; &#39;;
  
  thread.start(); //启动线程
 }
 
 public void run() {
  if (c==&#39; &#39;) return; //若不是数字字符直接结束
  //为便于观察,不同数字之前的空格数目不同,以便将其排在不同列上
  int spaceLen=c-&#39;0&#39;;
  StringBuffer s=new StringBuffer(spaceLen+1);
  for (int i=0; i<spaceLen; i++) s.append(&#39; &#39;);
  s.append(c);
  
  while (isEmitting[c]) {
    System.out.println(s); //不断地向屏幕写字符
  }
 }
 
/**实现线程状态监听者接口中的方法*/
 public void onRunOver(ReusableThread t) {
  myHomePool.recycle(this); //回收自身入池
 }
}
 
 
 
public class TestThreadPool {
 
public static void main(String[] args) {
 // TODO Auto-generated method stub
 //标示字符是否正被发射的标志变量数组
 boolean[] isEmitting=new boolean[256];
 for (int i=0; i<256; i++) isEmitting[i]=false;
  
 ObjectPool emitters=new ObjectPool(10); //新建对象池,容量为10
 for (int i=0; i<10; i++) {
 //用CharEmitter对象填满池子
 emitters.setElementAt(new CharEmitter(emitters, isEmitting), i);
 }
  
 byte[] c=new byte[1];
 CharEmitter emitter;
  
 while(true) {
 try {
 System.in.read(c); //从键盘读入一个字符,以回车键表示输入结束
 } catch(Exception e) {e.printStackTrace();}
  
 if (isEmitting[c[0]]) {
 isEmitting[c[0]]=false; //若字符正被发射,则结束其发射
 }
 else {
 isEmitting[c[0]]=true;
 emitter=(CharEmitter)emitters.fetch(); //向池中索取一个CharEmitter对象
 emitter.emit((char)c[0]); //发射用户输入的字符
 }
 }
}
 
}

執行後,從鍵盤上敲進0到9之間的任意數字並按回車,之後會不斷地在屏幕上滾動顯示該數字;再次輸入相同的數字則不再顯示該數字。當同時存在多個數字被發射時,可以明顯看出不同數字的顯示是交錯進行的,這正是由於虛擬機器在各線程間調度的結果。運行結果表明,我們設計的類別功能完全正確。

在以後要說的J2ME中藍牙通訊的輔助類別中,將會看到,線程池與可重啟線程起到了不可替代的作用。

以上這篇java 可重啟線程及線程池類的設計(詳解)就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持PHP中文網。

更多java 可重啟線程及線程池類的設計(詳解)相關文章請關注PHP中文網!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
在Java應用程序中緩解平台特定問題的策略是什麼?在Java應用程序中緩解平台特定問題的策略是什麼?May 01, 2025 am 12:20 AM

Java如何緩解平台特定的問題? Java通過JVM和標準庫來實現平台無關性。 1)使用字節碼和JVM抽像操作系統差異;2)標準庫提供跨平台API,如Paths類處理文件路徑,Charset類處理字符編碼;3)實際項目中使用配置文件和多平台測試來優化和調試。

Java的平台獨立性與微服務體系結構之間有什麼關係?Java的平台獨立性與微服務體系結構之間有什麼關係?May 01, 2025 am 12:16 AM

java'splatformentenceenhancesenhancesmicroservicesharchitecture byferingDeploymentFlexible,一致性,可伸縮性和便攜性。 1)DeploymentFlexibilityAllowsibilityAllowsOllowsOllowSorlowsOllowsOllowsOllowSeStorunonAnyPlatformwithajvM.2)penterencyCrossServAccAcrossServAcrossServiCessImplifififiesDeevelopmentandeDe

GRAALVM與Java的平台獨立目標有何關係?GRAALVM與Java的平台獨立目標有何關係?May 01, 2025 am 12:14 AM

GraalVM通過三種方式增強了Java的平台獨立性:1.跨語言互操作,允許Java與其他語言無縫互操作;2.獨立的運行時環境,通過GraalVMNativeImage將Java程序編譯成本地可執行文件;3.性能優化,Graal編譯器生成高效的機器碼,提升Java程序的性能和一致性。

您如何測試Java應用程序的平台兼容性?您如何測試Java應用程序的平台兼容性?May 01, 2025 am 12:09 AM

效率testjavaapplicationsforplatformcompatibility oftheSesteps:1)setUpautomatedTestingTestingActingAcrossMultPlatFormSusingCitoolSlikeSlikeJenkinSorgithUbactions.2)contuctualtemualtemalualTesteTESTENRETESTINGINREALHARTWARETOLEALHARDOELHARDOLEATOCATCHISSUSESUSEUSENINCIENVIRENTMENTS.3)schictcross.3)schoscross.3)

Java編譯器(Javac)在實現平台獨立性中的作用是什麼?Java編譯器(Javac)在實現平台獨立性中的作用是什麼?May 01, 2025 am 12:06 AM

Java編譯器通過將源代碼轉換為平台無關的字節碼,實現了Java的平台獨立性,使得Java程序可以在任何安裝了JVM的操作系統上運行。

在平台獨立性的平台獨立性上使用字節碼優於本機代碼的優點是什麼?在平台獨立性的平台獨立性上使用字節碼優於本機代碼的優點是什麼?Apr 30, 2025 am 12:24 AM

ByteCodeachievesPlatFormIndenceByByByByByByExecutedBoviratualMachine(VM),允許CodetorunonanyplatformwithTheApprepreprepvm.Forexample,Javabytecodecodecodecodecanrunonanydevicewithajvm

Java真的100%獨立於平台嗎?為什麼或為什麼不呢?Java真的100%獨立於平台嗎?為什麼或為什麼不呢?Apr 30, 2025 am 12:18 AM

Java不能做到100%的平台獨立性,但其平台獨立性通過JVM和字節碼實現,確保代碼在不同平台上運行。具體實現包括:1.編譯成字節碼;2.JVM的解釋執行;3.標準庫的一致性。然而,JVM實現差異、操作系統和硬件差異以及第三方庫的兼容性可能影響其平台獨立性。

Java的平台獨立性如何支持代碼可維護性?Java的平台獨立性如何支持代碼可維護性?Apr 30, 2025 am 12:15 AM

Java通過“一次編寫,到處運行”實現平台獨立性,提升代碼可維護性:1.代碼重用性高,減少重複開發;2.維護成本低,只需一處修改;3.團隊協作效率高,方便知識共享。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。