Maison  >  Article  >  Java  >  Conception de threads redémarrables Java et de classes de pool de threads (explication détaillée)

Conception de threads redémarrables Java et de classes de pool de threads (explication détaillée)

高洛峰
高洛峰original
2017-01-23 16:18:051433parcourir

Quiconque connaît la programmation multithread JAVA sait qu'il existe deux façons de générer un thread. L'une est que la classe hérite directement de la classe Thread et implémente sa méthode run() ; et implémente sa méthode run() , puis crée un nouveau Thread avec cette classe comme paramètre constructeur, similaire à la forme suivante : Thread t=new Thread(myRunnable). Au final, le thread est démarré en exécutant la méthode start() de la classe Thread.

En JAVA, une fois qu'un thread a fini de s'exécuter, c'est-à-dire après avoir exécuté sa méthode run(), il ne peut pas être redémarré. A cette époque, cet objet thread est devenu un objet inutile, en attente d'être recyclé par le garbage collector. La prochaine fois que vous souhaiterez redémarrer ce fil, vous devrez créer un nouvel objet fil et le redémarrer. La création et la destruction fréquentes d'objets affectent non seulement l'efficacité opérationnelle, mais peuvent également générer une grande quantité de mémoire inutile, car les objets thread inutiles n'ont pas le temps d'être recyclés. Cet impact est particulièrement important sur les plates-formes mobiles où l'espace de stockage et la vitesse de traitement sont relativement limités. . Alors, pouvez-vous repenser une classe de thread afin qu'elle puisse être démarrée à plusieurs reprises sans créer et détruire fréquemment des objets ?

Bien sûr. Ensuite, je présenterai la conception de cette classe « thread redémarrable ».

Tout d'abord, il doit être clair que si vous mettez toujours la tâche que vous souhaitez que le thread fasse directement dans la méthode run() du thread, vous ne pourrez de toute façon pas atteindre l'objectif, car comme mentionné ci-dessus, JAVA Une fois que la classe thread a exécuté la méthode run(), elle ne peut plus être redémarrée. Ainsi, le seul moyen possible est de placer la méthode run() du programme utilisateur (peut-être appelée "processus utilisateur") dans le corps de la boucle while à l'intérieur de la méthode run() réelle du thread, et de faire attendre le thread après l'exécution du processus utilisateur. . Lorsque la méthode restart est appelée pour redémarrer un thread, elle réveille en fait le thread en attente pour démarrer la boucle while suivante. L'idée générale est déterminée, et le code suivant est facile à comprendre :

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

Le code est facile à comprendre, non ? Mais expliquez pourquoi il existe une interface « écouteur de statut ». Parfois, nous souhaitons recevoir une notification en temps opportun après la fin du processus utilisateur afin de pouvoir effectuer un traitement supplémentaire. Dans ce cas, la méthode onRunOver de l'écouteur d'état est utile. Un exemple intuitif est que dans la classe "pool de threads" mentionnée ci-dessous, un thread redémarrable doit être automatiquement renvoyé au pool après l'exécution d'un processus utilisateur. À ce stade, l'action de retour au pool peut être placée dans la méthode onRunOver. , et son paramètre est l'objet thread redémarrable, donc l'objet indiqué par le paramètre peut être recyclé dans le pool de threads.

Quant à la classe pool de threads, il s'agit en fait d'une sous-classe de la classe pool d'objets mentionnée précédemment, et tous les objets qu'elle contient appartiennent à la classe RéutilisableThread. De plus, il implémente l'interface RéutilisableThread.ThreadStateListener afin qu'il puisse être averti à temps lorsque le processus utilisateur se termine et effectuer le travail de recyclage du fil :

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

Bien sûr, certaines fonctions peuvent doit être ajouté, car comme il s'agit simplement d'une classe de thread "améliorée" redémarrable de plus que les threads ordinaires, les fonctions de la classe Thread d'origine devraient également être disponibles, comme la fonction sleep() du thread. Cependant, ceux-ci sont relativement simples et seront omis ici.

Écrivez le programme de test ci-dessous. Je prévois de procéder ainsi : au lieu d'utiliser la classe pool de threads, j'effectue un test conjoint sur la classe pool d'objets et la classe thread redémarrable. La classe CharEmitter à laquelle appartient l'objet du pool d'objets implémente l'interface Runnable et le thread. interface d'écoute d'état et contient un objet membre de thread redémarrable qui n'est inclus dans aucun objet de pool de threads mais est utilisé indépendamment. Lorsque le processus utilisateur de ce thread (défini dans la classe CharEmitter) se termine, la méthode onRunOver effectue l'action de recycler cet objet CharEmitter dans le pool. Cela joue également le rôle de tester indirectement la classe du pool de threads, car la différence avec le pool d'objets est qu'il effectue des actions de recyclage dans onRunOver.

Il est préférable d'aller directement dans le code pour que ce soit clair :

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

Après l'exécution, tapez n'importe quel nombre entre 0 et 9 du clavier et Après avoir appuyé sur Entrée, le numéro continuera à défiler sur l'écran; si vous saisissez à nouveau le même numéro, le numéro ne sera plus affiché. Lorsque plusieurs numéros sont transmis en même temps, on voit clairement que l'affichage des différents numéros est entrelacé. C'est précisément le résultat de la planification de la machine virtuelle entre les threads. Les résultats montrent que la fonction de classe que nous avons conçue est tout à fait correcte.

Dans les classes auxiliaires pour la communication Bluetooth dans J2ME qui seront abordées plus tard, nous verrons que les pools de threads et les threads redémarrables jouent un rôle irremplaçable.

L'article ci-dessus sur la conception des threads redémarrables Java et des classes de pool de threads (explication détaillée) est tout le contenu partagé par l'éditeur. J'espère qu'il pourra vous donner une référence et j'espère que vous prendrez en charge PHP. Site chinois.

Pour des articles plus pertinents sur la conception de threads redémarrables Java et de classes de pool de threads (explication détaillée), veuillez faire attention au site Web PHP chinois !

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn