一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - java 可重啟線程及線程池類的設計(詳解)

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

2020-07-30 16:09轉載joyqi Java教程

下面小編就為大家帶來一篇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循環。大致的思想確定了,下面的代碼就很好理解了:

java" id="highlighter_488001">
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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接口,以便可以在用戶過程結束時及時收到通知,執行回收線程的工作:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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 :

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**字符放射器*/
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) {
  //字符被要求只能是'0'到'9'之間的數字字符
  if (ch>='0' && ch<='9') {
   c=ch;
  }
  else c=' ';
 
  thread.start(); //啟動線程
 }
 
 public void run() {
  if (c==' ') return; //若不是數字字符直接結束
  //為便于觀察,不同數字之前的空格數目不同,以便將其排在不同列上
  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); //不斷地向屏幕寫字符
  }
 }
 
/**實現線程狀態監聽者接口中的方法*/
 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 可重啟線程及線程池類的設計(詳解)就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持服務器之家。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日本韩国一区二区三区 | 色男人网| 午夜精品久久久久 | 欧美日韩中文字幕在线视频 | 精品一成人岛国片在线观看 | 男生操女生漫画 | 91国内精品久久久久影院优播 | 俄罗斯激情性孕妇孕交大全 | 四虎影院永久网站 | 免费a视频在线观看 | 亚洲欧美日韩精品久久亚洲区 | 成人永久免费福利视频网站 | 99久久免费视频 | 精品国产自在现线拍400部 | 五月丁香啪啪. | 欧美精选视频 | 男女刺激高清视频在线观看 | 美女下面揉出水免费视频 | 我的男友是消防员在线观看 | 清纯唯美 亚洲 | 短篇小说肉 | 新影音先锋男人色资源网 | 欧美日韩国产另类一区二区三区 | 精品一区二区免费视频蜜桃网 | 男女真实无遮挡xx00动态图软件 | 青视频在线 | 日韩欧美色| 日韩在线免费看 | 国产中文字幕 | 胸大的姑娘中文字幕视频 | 福利视频久久 | 亚洲高清中文字幕精品不卡 | 女攻双性 | 久久这里只有精品视频9 | 91一区二区在线观看精品 | 好大好猛好爽好深视频免费 | 很很草| 特黄特黄一级高清免费大片 | 国产网站免费看 | 成年人视频免费在线播放 | 高清日韩在线 |