欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java 可重啟線程及線程池類的設(shè)計(jì)(詳解)

 更新時(shí)間:2017年01月22日 08:47:39   投稿:jingxian  
下面小編就為大家?guī)硪黄猨ava 可重啟線程及線程池類的設(shè)計(jì)(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

了解JAVA多線程編程的人都知道,要產(chǎn)生一個(gè)線程有兩種方法,一是類直接繼承Thread類并實(shí)現(xiàn)其run()方法;二是類實(shí)現(xiàn)Runnable接口并實(shí)現(xiàn)其run()方法,然后新建一個(gè)以該類為構(gòu)造方法參數(shù)的Thread,類似于如下形式: Thread t=new Thread(myRunnable)。而最終使線程啟動(dòng)都是執(zhí)行Thread類的start()方法。

在JAVA中,一個(gè)線程一旦運(yùn)行完畢,即執(zhí)行完其run()方法,就不可以重新啟動(dòng)了。此時(shí)這個(gè)線程對象也便成了無用對象,等待垃圾回收器的回收。下次想再啟動(dòng)這個(gè)線程時(shí),必須重新new出一個(gè)線程對象再start之。頻繁地創(chuàng)建和銷毀對象不僅影響運(yùn)行效率,還可能因無用線程對象來不及被回收而產(chǎn)生大量的垃圾內(nèi)存,在存儲(chǔ)空間和處理速度都相對受限的移動(dòng)平臺(tái)上這種影響尤為顯著。那么,能否重新設(shè)計(jì)一種線程類,使其能夠被反復(fù)啟動(dòng)而無需頻繁地創(chuàng)建和銷毀對象呢?

當(dāng)然可以。下面我就介紹一下對這個(gè)“可重啟線程”類的設(shè)計(jì)。

首先必須明確,如果仍是把想要線程去做的任務(wù)直接放在線程的run()方法中,是無論如何無法達(dá)成目的的,因?yàn)榫拖裆厦嬉呀?jīng)說的,JAVA的線程類一旦執(zhí)行完run()方法就無法再啟動(dòng)了。所以唯一可行的辦法是,把用戶程序要做的run()方法(不妨稱作“用戶過程”)套在線程實(shí)際的run()方法內(nèi)部的while循環(huán)體內(nèi),當(dāng)用戶過程執(zhí)行完后使線程wait。當(dāng)調(diào)用restart方法重啟線程時(shí),實(shí)際就是喚醒等待中的線程使之開始下一次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; //用戶過程已啟動(dòng)
 public static final byte STATE_DESTROYED=2; //線程最終銷毀
 
 byte mState; //標(biāo)示可重啟線程的當(dāng)前狀態(tài)
 
 Thread mThread; //實(shí)際的主線程對象
 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)時(shí)調(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é)束而進(jìn)入等待中的主線程
   return true;
  }
  else
   return false;
 }
 
 /**結(jié)束整個(gè)線程,銷毀主線程對象。之后將不可再次啟動(dòng)*/
 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;
   }
  }
 }
 
}

代碼很好懂是不是?但是要解釋一下為什么要有一個(gè)“狀態(tài)監(jiān)聽者”接口。有時(shí)候我們可能想要在用戶過程結(jié)束后得到一個(gè)及時(shí)的通知,好進(jìn)行另外的處理,這時(shí)狀態(tài)監(jiān)聽者的onRunOver方法就有了用處。一個(gè)直觀的例子是,在下面要提到的“線程池”類中,一個(gè)可重啟線程執(zhí)行完一次用戶過程后應(yīng)當(dāng)自動(dòng)回收入池,這時(shí)就可以把回收入池的動(dòng)作放在onRunOver方法中,而它的參數(shù)就是該可重啟線程對象,于是就可以把參數(shù)所指示的對象回收進(jìn)線程池中。
 

至于線程池類,其實(shí)就是以前提到的對象池類的一個(gè)子類,其中的對象全是ReusableThread類的。另外它實(shí)現(xiàn)了ReusableThread.ThreadStateListener接口,以便可以在用戶過程結(jié)束時(shí)及時(shí)收到通知,執(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é)束時(shí),回收線程
 }
 
 private void init(int size) {
  ReusableThread thread;
  //初始化線程池內(nèi)容
  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);
 }
 
}

當(dāng)然,還有一些可能需要添加的功能,因?yàn)榧热恢皇潜绕胀ň€程多了一個(gè)可重啟的“增強(qiáng)”型線程類,那么原來Thread類具有的功能也應(yīng)該具有,比如線程的sleep()。不過那些比較簡單,這里就略去了。

下面編寫測試程序。我準(zhǔn)備這樣進(jìn)行:并不用到線程池類,而是對對象池類和可重啟線程類進(jìn)行聯(lián)合測試,該對象池中的對象所屬的類CharEmitter實(shí)現(xiàn)了Runnable接口和線程狀態(tài)監(jiān)聽者接口,并且含有一個(gè)可重啟線程成員對象,它并不包含在任何線程池對象中,而是獨(dú)立使用的。當(dāng)此線程的用戶過程(定義在CharEmitter類中)結(jié)束后,onRunOver方法執(zhí)行回收本CharEmitter對象入池的動(dòng)作。這樣就同時(shí)起到了間接測試線程池類的作用,因?yàn)樗c對象池的區(qū)別也不過是在onRunOver中執(zhí)行回收動(dòng)作而已。

還是直接上代碼說得清楚:

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)把自己回收到哪里,需要保存一個(gè)到自己所在對象池的引用

 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(); //啟動(dòng)線程
 }

 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<spaceLen; i++) s.append(' ');
  s.append(c);
 
  while (isEmitting[c]) {
    System.out.println(s); //不斷地向屏幕寫字符
  }
 }

/**實(shí)現(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); //從鍵盤讀入一個(gè)字符,以回車鍵表示輸入結(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(); //向池中索取一個(gè)CharEmitter對象
 emitter.emit((char)c[0]); //發(fā)射用戶輸入的字符
 }
 }
}

}

執(zhí)行后,從鍵盤上敲進(jìn)0到9之間的任意數(shù)字并按回車,之后會(huì)不斷地在屏幕上滾動(dòng)顯示該數(shù)字;再次輸入同樣的數(shù)字則不再顯示該數(shù)字。同時(shí)存在多個(gè)數(shù)字被發(fā)射時(shí),可以明顯看出不同數(shù)字的顯示是交錯(cuò)進(jìn)行的,這正是由于虛擬機(jī)在各線程間調(diào)度的結(jié)果。運(yùn)行結(jié)果表明,我們設(shè)計(jì)的類功能完全正確。

在以后要說的J2ME中藍(lán)牙通訊的輔助類中,將會(huì)看到,線程池與可重啟線程起到了不可替代的作用。

以上這篇java 可重啟線程及線程池類的設(shè)計(jì)(詳解)就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 實(shí)戰(zhàn)干貨之基于SpringBoot的RabbitMQ多種模式隊(duì)列

    實(shí)戰(zhàn)干貨之基于SpringBoot的RabbitMQ多種模式隊(duì)列

    RabbitMQ 是一個(gè)由Erlang語言開發(fā)的AMQP的開源實(shí)現(xiàn),支持多種客戶端。用于在分布式系統(tǒng)中存儲(chǔ)轉(zhuǎn)發(fā)消息,在易用性、擴(kuò)展性、高可用性等方面表現(xiàn)不俗,下文將帶你深入了解 RabbitMQ 多種模式隊(duì)列
    2021-09-09
  • SpringBoot配置Redis實(shí)現(xiàn)保存獲取和刪除數(shù)據(jù)

    SpringBoot配置Redis實(shí)現(xiàn)保存獲取和刪除數(shù)據(jù)

    本文主要介紹了SpringBoot配置Redis實(shí)現(xiàn)保存獲取和刪除數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • Java+Selenium實(shí)現(xiàn)文件上傳下載功能詳解

    Java+Selenium實(shí)現(xiàn)文件上傳下載功能詳解

    這篇文章主要介紹了java代碼如何利用selenium操作瀏覽器上傳和下載文件功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下
    2023-01-01
  • SpringBoot項(xiàng)目多層級多環(huán)境yml設(shè)計(jì)詳解

    SpringBoot項(xiàng)目多層級多環(huán)境yml設(shè)計(jì)詳解

    這篇文章主要為大家介紹了SpringBoot項(xiàng)目多層級多環(huán)境yml設(shè)計(jì)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • Java matches類,Pattern類及matcher類用法示例

    Java matches類,Pattern類及matcher類用法示例

    這篇文章主要介紹了Java matches類,Pattern類及matcher類用法,結(jié)合實(shí)例形式分析了java matches類,Pattern類及matcher類針對字符串常見操作技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2019-03-03
  • Java開發(fā)SSM框架微信退款的實(shí)現(xiàn)

    Java開發(fā)SSM框架微信退款的實(shí)現(xiàn)

    這篇文章是Java微信退款的教程,退款之前用戶需要先進(jìn)行支付,支付之后才可以使用退款,非常具有實(shí)用價(jià)值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • Springboot整合logback多節(jié)點(diǎn)日志文件加端口號區(qū)分的操作方法

    Springboot整合logback多節(jié)點(diǎn)日志文件加端口號區(qū)分的操作方法

    這篇文章主要介紹了Springboot整合logback多節(jié)點(diǎn)日志文件加端口號區(qū)分的操作方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • SpringBoot自定義注解之實(shí)現(xiàn)AOP切面日志詳解

    SpringBoot自定義注解之實(shí)現(xiàn)AOP切面日志詳解

    這篇文章主要為大家詳細(xì)介紹了SpringBoot自定義注解之實(shí)現(xiàn)AOP切面統(tǒng)一打印出入?yún)⑷罩荆闹惺纠a介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • java8保姆級lambda表達(dá)式教程

    java8保姆級lambda表達(dá)式教程

    這篇文章主要介紹了Java8之后的Lambda表達(dá)式的用法,lambda表達(dá)式將大量替代匿名內(nèi)部類的使用,簡化代碼的同時(shí),更突出了原來匿名內(nèi)部類中最重要的那部分包含真正邏輯的代碼,需要的朋友可以參考下
    2023-03-03
  • SpringBoot整合FreeMarker的過程詳解

    SpringBoot整合FreeMarker的過程詳解

    FreeMarker 是一個(gè)模板引擎,可以將模板與數(shù)據(jù)結(jié)合生成文本輸出,本文給大家介紹SpringBoot整合FreeMarker的過程,感興趣的朋友一起看看吧
    2024-01-01

最新評論