java 重启线程_java 可重启线程及线程池类的设计(详解)
了解JAVA多線程編程的人都知道,要產(chǎn)生一個線程有兩種方法,一是類直接繼承Thread類并實現(xiàn)其run()方法;二是類實現(xiàn)Runnable接口并實現(xiàn)其run()方法,然后新建一個以該類為構(gòu)造方法參數(shù)的Thread,類似于如下形式: Thread t=new Thread(myRunnable)。而最終使線程啟動都是執(zhí)行Thread類的start()方法。
在JAVA中,一個線程一旦運行完畢,即執(zhí)行完其run()方法,就不可以重新啟動了。此時這個線程對象也便成了無用對象,等待垃圾回收器的回收。下次想再啟動這個線程時,必須重新new出一個線程對象再start之。頻繁地創(chuàng)建和銷毀對象不僅影響運行效率,還可能因無用線程對象來不及被回收而產(chǎn)生大量的垃圾內(nèi)存,在存儲空間和處理速度都相對受限的移動平臺上這種影響尤為顯著。那么,能否重新設(shè)計一種線程類,使其能夠被反復(fù)啟動而無需頻繁地創(chuàng)建和銷毀對象呢?
當(dāng)然可以。下面我就介紹一下對這個“可重啟線程”類的設(shè)計。
首先必須明確,如果仍是把想要線程去做的任務(wù)直接放在線程的run()方法中,是無論如何無法達(dá)成目的的,因為就像上面已經(jīng)說的,JAVA的線程類一旦執(zhí)行完run()方法就無法再啟動了。所以唯一可行的辦法是,把用戶程序要做的run()方法(不妨稱作“用戶過程”)套在線程實際的run()方法內(nèi)部的while循環(huán)體內(nèi),當(dāng)用戶過程執(zhí)行完后使線程wait。當(dāng)調(diào)用restart方法重啟線程時,實際就是喚醒等待中的線程使之開始下一次while循環(huán)。大致的思想確定了,下面的代碼就很好理解了:
public class ReusableThread implements Runnable {
//線程狀態(tài)監(jiān)聽者接口
public interface ThreadStateListener {
public abstract void onRunOver(ReusableThread thread);//當(dāng)用戶過程執(zhí)行完畢后調(diào)用的方法
}
public static final byte STATE_READY=0; //線程已準(zhǔn)備好,等待開始用戶過程
public static final byte STATE_STARTED=1; //用戶過程已啟動
public static final byte STATE_DESTROYED=2; //線程最終銷毀
byte mState; //標(biāo)示可重啟線程的當(dāng)前狀態(tài)
Thread mThread; //實際的主線程對象
Runnable mProc; //用戶過程的run()方法定義在mProc中
ThreadStateListener mListener; //狀態(tài)監(jiān)聽者,可以為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;
}
/**可以在處于等待狀態(tài)時調(diào)用該方法重設(shè)用戶過程*/
public synchronized boolean setProcedure(Runnable proc) {
if (mState == STATE_READY) {
mProc = proc;
return true;
}
else
return false;
}
/**開始執(zhí)行用戶過程*/
public synchronized boolean start() {
if (mState == STATE_READY) {
mState = STATE_STARTED;
if (!mThread.isAlive()) mThread.start();
notify(); //喚醒因用戶過程執(zhí)行結(jié)束而進入等待中的主線程
return true;
}
else
return false;
}
/**結(jié)束整個線程,銷毀主線程對象。之后將不可再次啟動*/
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); //當(dāng)用戶過程結(jié)束后,執(zhí)行監(jiān)聽者的onRunOver方法
synchronized (this) {
if (mState == STATE_DESTROYED) return;
mState = STATE_READY;
}
}
}
}
代碼很好懂是不是?但是要解釋一下為什么要有一個“狀態(tài)監(jiān)聽者”接口。有時候我們可能想要在用戶過程結(jié)束后得到一個及時的通知,好進行另外的處理,這時狀態(tài)監(jiān)聽者的onRunOver方法就有了用處。一個直觀的例子是,在下面要提到的“線程池”類中,一個可重啟線程執(zhí)行完一次用戶過程后應(yīng)當(dāng)自動回收入池,這時就可以把回收入池的動作放在onRunOver方法中,而它的參數(shù)就是該可重啟線程對象,于是就可以把參數(shù)所指示的對象回收進線程池中。
至于線程池類,其實就是以前提到的對象池類的一個子類,其中的對象全是ReusableThread類的。另外它實現(xiàn)了ReusableThread.ThreadStateListener接口,以便可以在用戶過程結(jié)束時及時收到通知,執(zhí)行回收線程的工作:
public class ThreadPool extends ObjectPool implements ReusableThread.ThreadStateListener {
public static final int DefaultNumThreads = 16; //默認(rèn)池容量
public ReusableThread getThread() {
return (ReusableThread)fetch();
}
public void onRunOver(ReusableThread thread) {
recycle(thread); //當(dāng)用戶過程結(jié)束時,回收線程
}
private void init(int size) {
ReusableThread thread;
//初始化線程池內(nèi)容
for (int i=0; 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);
}
}
當(dāng)然,還有一些可能需要添加的功能,因為既然只是比普通線程多了一個可重啟的“增強”型線程類,那么原來Thread類具有的功能也應(yīng)該具有,比如線程的sleep()。不過那些比較簡單,這里就略去了。
下面編寫測試程序。我準(zhǔn)備這樣進行:并不用到線程池類,而是對對象池類和可重啟線程類進行聯(lián)合測試,該對象池中的對象所屬的類CharEmitter實現(xiàn)了Runnable接口和線程狀態(tài)監(jiān)聽者接口,并且含有一個可重啟線程成員對象,它并不包含在任何線程池對象中,而是獨立使用的。當(dāng)此線程的用戶過程(定義在CharEmitter類中)結(jié)束后,onRunOver方法執(zhí)行回收本CharEmitter對象入池的動作。這樣就同時起到了間接測試線程池類的作用,因為它與對象池的區(qū)別也不過是在onRunOver中執(zhí)行回收動作而已。
還是直接上代碼說得清楚:
TestThreadPool.java :
/**字符放射器*/
class CharEmitter implements Runnable, ReusableThread.ThreadStateListener {
char c; //被發(fā)射的字符
boolean[] isEmitting; //標(biāo)示某字符是否正被發(fā)射(直接以字符對應(yīng)的ASCII碼作下標(biāo)索引)
ReusableThread thread; //可重啟線程對象
ObjectPool myHomePool; //為知道應(yīng)把自己回收到哪里,需要保存一個到自己所在對象池的引用
CharEmitter(ObjectPool container, boolean[] isCharEmitting) {
isEmitting=isCharEmitting;
myHomePool=container;
thread=new ReusableThread(this); //新建可重啟線程對象,設(shè)其用戶過程為CharEmitter類自己定義的
}
/**開始“發(fā)射”字符*/
public void emit(char ch) {
//字符被要求只能是'0'到'9'之間的數(shù)字字符
if (ch>='0' && ch<='9') {
c=ch;
}
else c=' ';
thread.start(); //啟動線程
}
public void run() {
if (c==' ') return; //若不是數(shù)字字符直接結(jié)束
//為便于觀察,不同數(shù)字之前的空格數(shù)目不同,以便將其排在不同列上
int spaceLen=c-'0';
StringBuffer s=new StringBuffer(spaceLen+1);
for (int i=0; i
s.append(c);
while (isEmitting[c]) {
System.out.println(s); //不斷地向屏幕寫字符
}
}
/**實現(xiàn)線程狀態(tài)監(jiān)聽者接口中的方法*/
public void onRunOver(ReusableThread t) {
myHomePool.recycle(this); //回收自身入池
}
}
public class TestThreadPool {
public static void main(String[] args) {
// TODO Auto-generated method stub
//標(biāo)示字符是否正被發(fā)射的標(biāo)志變量數(shù)組
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); //從鍵盤讀入一個字符,以回車鍵表示輸入結(jié)束
} catch(Exception e) {e.printStackTrace();}
if (isEmitting[c[0]]) {
isEmitting[c[0]]=false; //若字符正被發(fā)射,則結(jié)束其發(fā)射
}
else {
isEmitting[c[0]]=true;
emitter=(CharEmitter)emitters.fetch(); //向池中索取一個CharEmitter對象
emitter.emit((char)c[0]); //發(fā)射用戶輸入的字符
}
}
}
}
執(zhí)行后,從鍵盤上敲進0到9之間的任意數(shù)字并按回車,之后會不斷地在屏幕上滾動顯示該數(shù)字;再次輸入同樣的數(shù)字則不再顯示該數(shù)字。同時存在多個數(shù)字被發(fā)射時,可以明顯看出不同數(shù)字的顯示是交錯進行的,這正是由于虛擬機在各線程間調(diào)度的結(jié)果。運行結(jié)果表明,我們設(shè)計的類功能完全正確。
在以后要說的J2ME中藍(lán)牙通訊的輔助類中,將會看到,線程池與可重啟線程起到了不可替代的作用。
以上這篇java 可重啟線程及線程池類的設(shè)計(詳解)就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
總結(jié)
以上是生活随笔為你收集整理的java 重启线程_java 可重启线程及线程池类的设计(详解)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java数组 序列化_程序猿的日常——J
- 下一篇: java import路径_Java i