Heim  >  Artikel  >  Java  >  Design von Java-neustartbaren Threads und Thread-Pool-Klassen (ausführliche Erklärung)

Design von Java-neustartbaren Threads und Thread-Pool-Klassen (ausführliche Erklärung)

高洛峰
高洛峰Original
2017-01-23 16:18:051385Durchsuche

Jeder, der sich mit der JAVA-Multithread-Programmierung auskennt, weiß, dass es zwei Möglichkeiten gibt, einen Thread zu generieren. Die eine besteht darin, dass die Klasse die Thread-Klasse direkt erbt und ihre run()-Methode implementiert und implementiert seine run()-Methode und erstellt dann einen neuen Thread mit dieser Klasse als Konstruktorparameter, ähnlich der folgenden Form: Thread t=new Thread(myRunnable). Am Ende wird der Thread durch Ausführen der start()-Methode der Thread-Klasse gestartet.

Sobald ein Thread in JAVA seine Ausführung beendet hat, d. h. nachdem seine run()-Methode ausgeführt wurde, kann er nicht mehr neu gestartet werden. Zu diesem Zeitpunkt ist dieses Thread-Objekt zu einem nutzlosen Objekt geworden, das auf die Wiederverwertung durch den Garbage Collector wartet. Wenn Sie diesen Thread das nächste Mal erneut starten möchten, müssen Sie ein neues Thread-Objekt erstellen und es erneut starten. Das häufige Erstellen und Zerstören von Objekten beeinträchtigt nicht nur die Betriebseffizienz, sondern kann auch eine große Menge an Speichermüll erzeugen, da nutzlose Thread-Objekte keine Zeit zum Recycling haben. Diese Auswirkung ist besonders wichtig auf mobilen Plattformen, auf denen Speicherplatz und Verarbeitungsgeschwindigkeit relativ begrenzt sind. Können Sie also eine Thread-Klasse so umgestalten, dass sie wiederholt gestartet werden kann, ohne häufig Objekte zu erstellen und zu zerstören?

Natürlich. Als nächstes werde ich das Design dieser „neustartbaren Thread“-Klasse vorstellen.

Zunächst muss klar sein, dass Sie den Zweck sowieso nicht erreichen können, wenn Sie die Aufgabe, die der Thread erledigen soll, direkt in die run()-Methode des Threads stellen, weil Wie oben erwähnt, JAVA Sobald die Thread-Klasse die run()-Methode ausgeführt hat, kann sie nicht erneut gestartet werden. Der einzig mögliche Weg besteht also darin, die run()-Methode des Benutzerprogramms (vielleicht als „Benutzerprozess“ bezeichnet) in den While-Schleifenkörper innerhalb der eigentlichen run()-Methode des Threads einzufügen Warten . Wenn die Methode restart aufgerufen wird, um einen Thread neu zu starten, weckt sie tatsächlich den wartenden Thread auf, um die nächste while-Schleife zu starten. Die allgemeine Idee ist festgelegt und der folgende Code ist leicht zu verstehen:

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;
   }
  }
 }
  
}

Der Code ist leicht zu verstehen, oder? Aber erklären Sie, warum es eine „Status-Listener“-Schnittstelle gibt. Manchmal möchten wir möglicherweise eine rechtzeitige Benachrichtigung erhalten, nachdem der Benutzerprozess beendet ist, damit wir eine weitere Verarbeitung durchführen können. In diesem Fall ist die onRunOver-Methode des Status-Listeners nützlich. Ein intuitives Beispiel ist, dass in der unten erwähnten Klasse „Thread-Pool“ ein neu startbarer Thread nach der Ausführung eines Benutzerprozesses automatisch an den Pool zurückgegeben werden soll. Zu diesem Zeitpunkt kann die Aktion der Rückkehr an den Pool in der onRunOver-Methode platziert werden. und sein Parameter ist das neustartbare Thread-Objekt, sodass das durch den Parameter angegebene Objekt in den Thread-Pool zurückgeführt werden kann.

Die Thread-Pool-Klasse ist eigentlich eine Unterklasse der zuvor erwähnten Objekt-Pool-Klasse, und alle darin enthaltenen Objekte gehören zur Klasse ReusableThread. Darüber hinaus implementiert es die Schnittstelle „ReusableThread.ThreadStateListener“, sodass es rechtzeitig benachrichtigt werden kann, wenn der Benutzerprozess endet, und die Arbeit des Recyclings des Threads ausführen kann:

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);
 }
  
}

Natürlich gibt es einige Funktionen, die möglicherweise funktionieren Es muss hinzugefügt werden, da es nur eine neustartbare „erweiterte“ Thread-Klasse als gewöhnliche Threads gibt, daher sollten auch die Funktionen der ursprünglichen Thread-Klasse verfügbar sein, z. B. die Funktion „sleep()“ des Threads. Diese sind jedoch relativ einfach und werden hier weggelassen.

Schreiben Sie das Testprogramm unten. Ich habe vor, so vorzugehen: Anstatt die Thread-Pool-Klasse zu verwenden, führe ich einen gemeinsamen Test für die Objekt-Pool-Klasse und die neustartbare Thread-Klasse durch. Die Klasse CharEmitter, zu der das Objekt im Objekt-Pool gehört, implementiert die Runnable-Schnittstelle und den Thread Status-Listener-Schnittstelle und enthält ein neu startbares Thread-Mitgliedsobjekt, das in keinem Thread-Pool-Objekt enthalten ist, aber unabhängig verwendet wird. Wenn der Benutzerprozess dieses Threads (definiert in der CharEmitter-Klasse) endet, führt die onRunOver-Methode die Aktion aus, dieses CharEmitter-Objekt in den Pool zurückzuführen. Dies spielt auch die Rolle des indirekten Testens der Thread-Pool-Klasse, da der Unterschied zum Objektpool darin besteht, dass er Recyclingaktionen in onRunOver durchführt.

Es ist besser, direkt zum Code zu gehen, um es klar zu machen:

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]); //发射用户输入的字符
 }
 }
}
 
}

Geben Sie nach der Ausführung eine beliebige Zahl zwischen 0 und 9 aus dem ein Nach dem Drücken der Eingabetaste läuft die Nummer weiter auf dem Bildschirm. Wenn Sie dieselbe Nummer erneut eingeben, wird die Nummer nicht mehr angezeigt. Wenn mehrere Zahlen gleichzeitig übertragen werden, ist deutlich zu erkennen, dass die Anzeige verschiedener Zahlen verschachtelt ist. Dies ist genau das Ergebnis der Planung der virtuellen Maschine zwischen Threads. Die laufenden Ergebnisse zeigen, dass die von uns entworfene Klassenfunktion völlig korrekt ist.

In den später besprochenen Hilfsklassen für die Bluetooth-Kommunikation in J2ME werden wir sehen, dass Thread-Pools und neu startbare Threads eine unersetzliche Rolle spielen.

Der obige Artikel über das Design von neustartbaren Java-Threads und Thread-Pool-Klassen (ausführliche Erklärung) ist der gesamte vom Herausgeber geteilte Inhalt. Ich hoffe, dass er Ihnen eine Referenz geben kann, und ich hoffe, dass Sie PHP unterstützen Chinesische Website.

Weitere relevante Artikel zum Design von Java-neustartbaren Threads und Thread-Pool-Klassen (ausführliche Erklärung) finden Sie auf der chinesischen PHP-Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn