Maison  >  Article  >  Java  >  Explication détaillée du démarrage, de l'interruption ou de la fin des threads dans la programmation multithread Java

Explication détaillée du démarrage, de l'interruption ou de la fin des threads dans la programmation multithread Java

高洛峰
高洛峰original
2017-01-05 15:37:101514parcourir

Démarrage du thread :
1. Explication de la différence entre start() et run()
start() : Sa fonction est de démarrer un nouveau thread, et le nouveau thread exécutera le run() correspondant. méthode. start() ne peut pas être appelé à plusieurs reprises.
run() : run() est comme une méthode membre ordinaire et peut être appelée à plusieurs reprises. Si vous appelez run() seul, run() sera exécuté dans le thread actuel et aucun nouveau thread ne sera démarré !
Ce qui suit est expliqué avec du code.

class MyThread extends Thread{
  public void run(){
    ...
  }
};
MyThread mythread = new MyThread();

mythread.start() démarrera un nouveau thread et exécutera la méthode run() dans le nouveau thread.
Mythread.run() exécutera directement la méthode run() dans le thread actuel et ne démarrera pas de nouveau thread pour exécuter run().

2. Exemple de la différence entre start() et run()
Ci-dessous, un exemple simple est utilisé pour démontrer la différence entre eux. Le code source est le suivant :

// Demo.java 的源码
class MyThread extends Thread{
  public MyThread(String name) {
    super(name);
  }
 
  public void run(){
    System.out.println(Thread.currentThread().getName()+" is running");
  }
};
 
public class Demo {
  public static void main(String[] args) {
    Thread mythread=new MyThread("mythread");
 
    System.out.println(Thread.currentThread().getName()+" call mythread.run()");
    mythread.run();
 
    System.out.println(Thread.currentThread().getName()+" call mythread.start()");
    mythread.start();
  }
}

Résultats en cours d'exécution :

main call mythread.run()
main is running
main call mythread.start()
mythread is running

Explication des résultats :
(1) Thread.currentThread().getName() est utilisé pour obtenir le nom du "thread actuel". Le thread actuel fait référence au thread dont l'exécution est planifiée dans le CPU.
(2) mythread.run() est appelé dans le "thread principal main", et la méthode run() s'exécute directement sur le "thread principal main".
(3) mythread.start() démarrera le "thread mythread". Après le démarrage du "thread mythread", la méthode run() sera appelée à ce moment, la méthode run() est en cours d'exécution ; "fil mythread".

Interruption et terminaison des threads

1. Interruption du thread : interruption()
La fonction de l'interruption() est d'interrompre ce thread.
Ce thread est autorisé à s'interrompre ; lorsque d'autres threads appellent la méthode interruption() de ce thread, les autorisations seront vérifiées via checkAccess(). Cela peut lancer une SecurityException.
Si ce fil est dans un état bloqué : appeler le thread wait(), wait(long) ou wait(long, int) le fera entrer dans l'état d'attente (bloqué), ou appeler le thread join(), join (long ), join(long, int), sleep(long), sleep(long, int) le mettra également dans un état de blocage. Si le thread appelle sa méthode interruption() alors qu'il est bloqué, son "statut interrompu" sera effacé et une InterruptedException sera reçue. Par exemple, un thread entre dans l'état bloqué via wait(), puis interrompt le thread via interrompu(); l'appel d'interruption() définira immédiatement l'indicateur d'interruption du thread sur "true", mais comme le thread est dans l'état bloqué, le "drapeau d'interruption" " sera immédiatement remis à " false " et en même temps, une exception InterruptedException sera générée.
Si un thread est bloqué dans un sélecteur et est interrompu par interruption(); l'indicateur d'interruption du thread sera défini sur true et il reviendra immédiatement de l'opération de sélection.
S'il ne tombe pas dans la situation ci-dessus, lorsque le thread est interrompu via interrompu(), son indicateur d'interruption sera défini sur "true".
Interrompre un « fil de discussion terminé » ne fait rien.

2. Arrêt du thread
Il est recommandé de ne plus utiliser les méthodes stop() et suspend() dans Thread en raison de leur insécurité inhérente !
Ci-dessous, je discuterai d'abord des méthodes de terminaison des threads respectivement dans « l'état bloqué » et « l'état d'exécution », puis je résumerai une méthode générale.
1. Terminez le fil dans « l'état bloqué »
Habituellement, nous terminons le fil dans « l'état bloqué » par « interruption ».
Lorsque le thread entre dans l'état de blocage en raison d'appels tels que sleep(), wait(), join() et d'autres méthodes ; si l'interruption() du thread est appelée à ce moment, l'indicateur d'interruption du thread est défini sur true ; . Parce qu'il est dans un état bloqué, la marque d'interruption sera effacée et une InterruptedException sera générée. Le fil peut être terminé en plaçant InterruptedException jusqu'à ce que cela soit approprié. Le formulaire est le suivant :

@Override
public void run() {
  try {
    while (true) {
      // 执行任务...
    }
  } catch (InterruptedException ie) {
    // 由于产生InterruptedException异常,退出while(true)循环,线程终止!
  }
}

Description : Exécuter en continu les tâches pendant while (true), when. Lorsque le thread est dans un état bloqué, l'appel de l'interruption() du thread génère une interruption InterruptedException. L'interruption est capturée à l'extérieur de while(true), sortant ainsi de la boucle while(true) !
Remarque : la capture d'InterruptedException est généralement placée en dehors du corps de la boucle while(true), de sorte que la boucle while(true) soit quittée lorsqu'une exception se produit. Sinon, si InterruptedException se trouve dans le corps de la boucle while(true), un traitement de sortie supplémentaire doit être ajouté. Le formulaire est le suivant :

@Override
public void run() {
  while (true) {
    try {
      // 执行任务...
    } catch (InterruptedException ie) {
      // InterruptedException在while(true)循环体内。
      // 当线程产生了InterruptedException异常时,while(true)仍能继续运行!需要手动退出
      break;
    }
  }
}

Description : L'exception InterruptedException ci-dessus est capturée dans whle(true). Lorsqu'une exception InterruptedException se produit, elle est toujours dans le corps de la boucle while(true) en dehors d'être gérée par catch ; pour quitter le corps de la boucle while(true), des opérations supplémentaires pour quitter while(true) sont nécessaires.
2. Terminez le thread en "état d'exécution"
Habituellement, nous terminons le thread en "état d'exécution" en le "marquant". Parmi eux, la « marque d'interruption » et la « marque d'ajout supplémentaire » sont incluses.
(1) Terminez le fil de discussion via "l'indicateur d'interruption".
Le format est le suivant :

@Override
public void run() {
  while (!isInterrupted()) {
    // 执行任务...
  }
}

说明:isInterrupted()是判断线程的中断标记是不是为true。当线程处于运行状态,并且我们需要终止它时;可以调用线程的interrupt()方法,使用线程的中断标记为true,即isInterrupted()会返回true。此时,就会退出while循环。
注意:interrupt()并不会终止处于“运行状态”的线程!它会将线程的中断标记设为true。
(2) 通过“额外添加标记”。
形式如下:

private volatile boolean flag= true;
protected void stopTask() {
  flag = false;
}
 
@Override
public void run() {
  while (flag) {
    // 执行任务...
  }
}

   

说明:线程中有一个flag标记,它的默认值是true;并且我们提供stopTask()来设置flag标记。当我们需要终止该线程时,调用该线程的stopTask()方法就可以让线程退出while循环。
注意:将flag定义为volatile类型,是为了保证flag的可见性。即其它线程通过stopTask()修改了flag之后,本线程能看到修改后的flag的值。
综合线程处于“阻塞状态”和“运行状态”的终止方式,比较通用的终止线程的形式如下:

@Override
public void run() {
  try {
    // 1. isInterrupted()保证,只要中断标记为true就终止线程。
    while (!isInterrupted()) {
      // 执行任务...
    }
  } catch (InterruptedException ie) {
    // 2. InterruptedException异常保证,当InterruptedException异常产生时,线程被终止。
  }
}

   

3. 终止线程的示例
interrupt()常常被用来终止“阻塞状态”线程。参考下面示例:

// Demo1.java的源码
class MyThread extends Thread {
 
  public MyThread(String name) {
    super(name);
  }
 
  @Override
  public void run() {
    try {
      int i=0;
      while (!isInterrupted()) {
        Thread.sleep(100); // 休眠100ms
        i++;
        System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);
      }
    } catch (InterruptedException e) {
      System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException.");
    }
  }
}
 
public class Demo1 {
 
  public static void main(String[] args) {
    try {
      Thread t1 = new MyThread("t1"); // 新建“线程t1”
      System.out.println(t1.getName() +" ("+t1.getState()+") is new.");
 
      t1.start();           // 启动“线程t1”
      System.out.println(t1.getName() +" ("+t1.getState()+") is started.");
 
      // 主线程休眠300ms,然后主线程给t1发“中断”指令。
      Thread.sleep(300);
      t1.interrupt();
      System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");
 
      // 主线程休眠300ms,然后查看t1的状态。
      Thread.sleep(300);
      System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

   

运行结果:

t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
t1 (TIMED_WAITING) is interrupted.
t1 (RUNNABLE) catch InterruptedException.
t1 (TERMINATED) is interrupted now.

   

结果说明:
(1) 主线程main中通过new MyThread("t1")创建线程t1,之后通过t1.start()启动线程t1。
(2) t1启动之后,会不断的检查它的中断标记,如果中断标记为“false”;则休眠100ms。
(3) t1休眠之后,会切换到主线程main;主线程再次运行时,会执行t1.interrupt()中断线程t1。t1收到中断指令之后,会将t1的中断标记设置“false”,而且会抛出InterruptedException异常。在t1的run()方法中,是在循环体while之外捕获的异常;因此循环被终止。
我们对上面的结果进行小小的修改,将run()方法中捕获InterruptedException异常的代码块移到while循环体内。

// Demo2.java的源码
class MyThread extends Thread {
 
  public MyThread(String name) {
    super(name);
  }
 
  @Override
  public void run() {
    int i=0;
    while (!isInterrupted()) {
      try {
        Thread.sleep(100); // 休眠100ms
      } catch (InterruptedException ie) {
        System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException.");
      }
      i++;
      System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);
    }
  }
}
 
public class Demo2 {
 
  public static void main(String[] args) {
    try {
      Thread t1 = new MyThread("t1"); // 新建“线程t1”
      System.out.println(t1.getName() +" ("+t1.getState()+") is new.");
 
      t1.start();           // 启动“线程t1”
      System.out.println(t1.getName() +" ("+t1.getState()+") is started.");
 
      // 主线程休眠300ms,然后主线程给t1发“中断”指令。
      Thread.sleep(300);
      t1.interrupt();
      System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");
 
      // 主线程休眠300ms,然后查看t1的状态。
      Thread.sleep(300);
      System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

   

运行结果:

t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
t1 (TIMED_WAITING) is interrupted.
t1 (RUNNABLE) catch InterruptedException.
t1 (RUNNABLE) loop 3
t1 (RUNNABLE) loop 4
t1 (RUNNABLE) loop 5
t1 (TIMED_WAITING) is interrupted now.
t1 (RUNNABLE) loop 6
t1 (RUNNABLE) loop 7
t1 (RUNNABLE) loop 8
t1 (RUNNABLE) loop 9
...

   

结果说明:
程序进入了死循环!
为什么会这样呢?这是因为,t1在“等待(阻塞)状态”时,被interrupt()中断;此时,会清除中断标记[即isInterrupted()会返回false],而且会抛出InterruptedException异常[该异常在while循环体内被捕获]。因此,t1理所当然的会进入死循环了。
解决该问题,需要我们在捕获异常时,额外的进行退出while循环的处理。例如,在MyThread的catch(InterruptedException)中添加break 或 return就能解决该问题。
下面是通过“额外添加标记”的方式终止“状态状态”的线程的示例:

// Demo3.java的源码
class MyThread extends Thread {
 
  private volatile boolean flag= true;
  public void stopTask() {
    flag = false;
  }
 
  public MyThread(String name) {
    super(name);
  }
 
  @Override
  public void run() {
    synchronized(this) {
      try {
        int i=0;
        while (flag) {
          Thread.sleep(100); // 休眠100ms
          i++;
          System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);
        }
      } catch (InterruptedException ie) {
        System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException.");
      }
    }
  }
}
 
public class Demo3 {
 
  public static void main(String[] args) {
    try {
      MyThread t1 = new MyThread("t1"); // 新建“线程t1”
      System.out.println(t1.getName() +" ("+t1.getState()+") is new.");
 
      t1.start();           // 启动“线程t1”
      System.out.println(t1.getName() +" ("+t1.getState()+") is started.");
 
      // 主线程休眠300ms,然后主线程给t1发“中断”指令。
      Thread.sleep(300);
      t1.stopTask();
      System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");
 
      // 主线程休眠300ms,然后查看t1的状态。
      Thread.sleep(300);
      System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

   

运行结果:

t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
t1 (TIMED_WAITING) is interrupted.
t1 (RUNNABLE) loop 3
t1 (TERMINATED) is interrupted now.

   


更多详解Java多线程编程中线程的启动、中断或终止操作相关文章请关注PHP中文网!


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