Heim  >  Artikel  >  Java  >  Detaillierte Erläuterung des Startens, Unterbrechens oder Beendens von Threads in der Java-Multithread-Programmierung

Detaillierte Erläuterung des Startens, Unterbrechens oder Beendens von Threads in der Java-Multithread-Programmierung

高洛峰
高洛峰Original
2017-01-05 15:37:101424Durchsuche

Thread-Start:
1. Erklärung des Unterschieds zwischen start() und run()
start(): Seine Funktion besteht darin, einen neuen Thread zu starten, und der neue Thread führt den entsprechenden run() aus Verfahren. start() kann nicht wiederholt aufgerufen werden.
run(): run() ist wie eine gewöhnliche Member-Methode und kann wiederholt aufgerufen werden. Wenn Sie run() alleine aufrufen, wird run() im aktuellen Thread ausgeführt und kein neuer Thread gestartet!
Das Folgende wird mit Code erklärt.

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

mythread.start() startet einen neuen Thread und führt die run()-Methode im neuen Thread aus.
Mythread.run() führt die run()-Methode direkt im aktuellen Thread aus und startet keinen neuen Thread, um run() auszuführen.

2. Beispiel für den Unterschied zwischen start() und run()
Nachfolgend wird ein einfaches Beispiel verwendet, um den Unterschied zwischen ihnen zu demonstrieren. Der Quellcode lautet wie folgt:

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

Laufergebnisse:

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

Ergebnisbeschreibung:
(1) Thread.currentThread().getName() wird verwendet, um den Namen des „aktuellen Threads“ abzurufen. Der aktuelle Thread bezieht sich auf den Thread, dessen Ausführung in der CPU geplant ist.
(2) mythread.run() wird im „Hauptthread main“ aufgerufen und die run()-Methode wird direkt im „Hauptthread main“ ausgeführt.
(3) mythread.start() startet den „Thread mythread“. Zu diesem Zeitpunkt wird die run()-Methode aufgerufen „Thread-Mythread“.

Unterbrechung und Beendigung von Threads

1. Thread-Unterbrechung: interrupt()
Die Funktion von interrupt() besteht darin, diesen Thread zu unterbrechen.
Dieser Thread darf sich selbst unterbrechen. Wenn andere Threads die Interrupt()-Methode dieses Threads aufrufen, werden die Berechtigungen über checkAccess() überprüft. Dies kann eine SecurityException auslösen.
Wenn sich dieser Thread in einem blockierten Zustand befindet: Der Aufruf von „wait()“, „wait(long)“ oder „wait(long, int)“ des Threads führt dazu, dass er in den Wartezustand (blockiert) wechselt, oder der Aufruf von „join()“ des Threads, „join“. (long), join(long, int), sleep(long), sleep(long, int) wird es ebenfalls in einen blockierenden Zustand versetzen. Wenn der Thread seine Interrupt()-Methode aufruft, während er blockiert ist, wird sein „Unterbrochener Status“ gelöscht und eine InterruptedException empfangen. Beispielsweise wechselt ein Thread über wait() in den blockierten Zustand und unterbricht ihn dann über interrupt(). Durch den Aufruf von interrupt() wird das Interrupt-Flag des Threads sofort auf „true“ gesetzt. Da sich der Thread jedoch im blockierten Zustand befindet, Das „Interrupt-Flag“ wird sofort auf „false“ zurückgesetzt und gleichzeitig wird eine InterruptedException generiert.
Wenn ein Thread in einem Selector blockiert ist und durch interrupt(); unterbrochen wird, wird das Interrupt-Flag des Threads auf true gesetzt und er kehrt sofort aus dem Auswahlvorgang zurück.
Wenn die obige Situation nicht auftritt und der Thread durch interrupt() unterbrochen wird, wird sein Interrupt-Flag auf „true“ gesetzt.
Das Unterbrechen eines „beendeten Threads“ hat keine Auswirkung.

2. Thread-Beendigung
Es wird empfohlen, die Methoden stop() und suspend() in Thread aufgrund ihrer inhärenten Unsicherheit nicht mehr zu verwenden!
Im Folgenden werde ich zunächst die Beendigungsmethoden von Threads im „blockierten Zustand“ bzw. im „laufenden Zustand“ diskutieren und dann eine allgemeine Methode zusammenfassen.
1. Beenden Sie den Thread im „blockierten Zustand“
Normalerweise beenden wir den Thread im „blockierten Zustand“ durch „Unterbrechung“.
Wenn der Thread aufgrund von Aufrufen wie Sleep (), Wait (), Join () und anderen Methoden zu diesem Zeitpunkt aufgerufen wird, wird das Interrupt-Flag des Threads auf true gesetzt . Da es sich in einem blockierten Zustand befindet, wird die Interrupt-Markierung gelöscht und eine InterruptedException generiert. Der Thread kann durch Platzieren einer InterruptedException beendet werden. Das Formular lautet wie folgt:

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

Beschreibung: Aufgaben kontinuierlich in while(true) ausführen Wenn sich der Thread in einem blockierten Zustand befindet, generiert der Aufruf von interrupt() des Threads einen InterruptedException-Interrupt. Der Interrupt wird außerhalb von while(true) erfasst, wodurch die while(true)-Schleife verlassen wird!

Hinweis: Die Erfassung von InterruptedException erfolgt im Allgemeinen außerhalb des while(true)-Schleifenkörpers, sodass die while(true)-Schleife beendet wird, wenn eine Ausnahme auftritt. Andernfalls muss eine zusätzliche Exit-Verarbeitung hinzugefügt werden, wenn sich InterruptedException innerhalb des while(true)-Schleifenkörpers befindet. Das Formular lautet wie folgt:


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

Beschreibung: Die obige InterruptedException-Ausnahme wird in whle(true) erfasst. Wenn eine InterruptedException-Ausnahme auftritt, befindet sie sich immer noch im Körper der while(true)-Schleife und wird nicht von Catch behandelt. Um den Körper der while(true)-Schleife zu verlassen, sind zusätzliche Vorgänge zum Verlassen von while(true) erforderlich.

2. Beenden Sie den Thread im „Laufzustand“

Normalerweise beenden wir den Thread im „Laufzustand“ durch „Markieren“. Darunter sind „Unterbrechungsmarke“ und „zusätzliche Ergänzungsmarke“.

(1) Beenden Sie den Thread durch „Interrupt-Flag“.
Das Format ist wie folgt:


@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中文网!


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