Maison Java javaDidacticiel 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)

Jan 23, 2017 pm 04:18 PM
java 线程池

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;
   }
  }
 }
  
}
Copier après la connexion

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);
 }
  
}
Copier après la connexion

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]); //发射用户输入的字符
 }
 }
}
 
}
Copier après la connexion

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 de ce site Web
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

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Dans le débogage à distance Java, comment obtenir correctement des valeurs constantes sur les serveurs distants? Dans le débogage à distance Java, comment obtenir correctement des valeurs constantes sur les serveurs distants? Apr 19, 2025 pm 01:54 PM

Questions et réponses sur l'acquisition constante dans le débogage à distance Java Lors de l'utilisation de Java pour un débogage à distance, de nombreux développeurs peuvent rencontrer des phénomènes difficiles. Il...

Comment choisir des outils de gestion de projet Java lors de l'apprentissage du développement back-end? Comment choisir des outils de gestion de projet Java lors de l'apprentissage du développement back-end? Apr 19, 2025 pm 02:15 PM

Confondu avec le choix des outils de gestion de projet Java pour les débutants. Pour ceux qui commencent tout juste à apprendre le développement backend, le choix des bons outils de gestion de projet est crucial ...

Lorsque Tomcat charge les modules de Spring-Web, le mécanisme SPI détruit-il vraiment le principe de visibilité des chargeurs de classe Java? Lorsque Tomcat charge les modules de Spring-Web, le mécanisme SPI détruit-il vraiment le principe de visibilité des chargeurs de classe Java? Apr 19, 2025 pm 02:18 PM

Analyse du comportement de chargement des classes du mécanisme SPI lorsque Tomcat charge les modules Spring-Web. Tomcat est utilisé pour découvrir et utiliser la servie fournie par Spring-Web lors du chargement des modules Spring-Web ...

Quelle est la raison pour laquelle le navigateur ne répond pas après le retour du serveur WebSocket 401? Comment le résoudre? Quelle est la raison pour laquelle le navigateur ne répond pas après le retour du serveur WebSocket 401? Comment le résoudre? Apr 19, 2025 pm 02:21 PM

La méthode non réactive du navigateur après le retour du serveur WebSocket 401. Lorsque vous utilisez Netty pour développer un serveur WebSocket, vous rencontrez souvent la nécessité de vérifier le jeton. � ...

Cohérence ultime dans les systèmes distribués: comment s'appliquer et comment compenser les incohérences des données? Cohérence ultime dans les systèmes distribués: comment s'appliquer et comment compenser les incohérences des données? Apr 19, 2025 pm 02:24 PM

L'exploration de l'application de la cohérence ultime dans les systèmes distribués Le traitement des transactions distribué a toujours été un problème dans l'architecture du système distribué. Pour résoudre le problème ...

Comment restreindre l'accès à des interfaces spécifiques des pages H5 imbriquées via le mécanisme de portée d'Oauth2.0? Comment restreindre l'accès à des interfaces spécifiques des pages H5 imbriquées via le mécanisme de portée d'Oauth2.0? Apr 19, 2025 pm 02:30 PM

Comment utiliser Access_token d'Oauth2.0 pour obtenir le contrôle des autorisations d'accès à l'interface? Dans l'application de OAuth2.0, comment s'assurer que le ...

En développement arrière, comment distinguer les responsabilités de la couche de service et de la couche DAO? En développement arrière, comment distinguer les responsabilités de la couche de service et de la couche DAO? Apr 19, 2025 pm 01:51 PM

Discuter de l'architecture hiérarchique dans le développement back-end. Dans le développement back-end, l'architecture hiérarchique est un modèle de conception courant, y compris généralement le contrôleur, le service et les trois couches DAO ...

Pourquoi signale-t-il une erreur lors de la soumission d'un travail de pyflink sur le fil? Pourquoi signale-t-il une erreur lors de la soumission d'un travail de pyflink sur le fil? Apr 19, 2025 pm 02:06 PM

Analyse de la raison pour laquelle le script Python ne peut être trouvé lors de la soumission d'un travail de pyflink sur le fil lorsque vous essayez de soumettre un travail de pyflink via le fil, vous pouvez rencontrer ...

See all articles