Java マルチスレッド プログラミングにおけるスレッドの開始、中断、終了の詳細な説明

高洛峰
リリース: 2017-01-05 15:37:10
オリジナル
1519 人が閲覧しました

スレッドの起動:
1. start() と run() の違いの説明
start(): その機能は新しいスレッドを開始することであり、新しいスレッドは対応する run() メソッドを実行します。 start() を繰り返し呼び出すことはできません。
run(): run() は通常のメンバー メソッドと同様で、繰り返し呼び出すことができます。 run() を単独で呼び出した場合、run() は現在のスレッドで実行され、新しいスレッドは開始されません。
以下はコードで説明します。

class MyThread extends Thread{
  public void run(){
    ...
  }
};
MyThread mythread = new MyThread();
ログイン後にコピー

mythread.start() は新しいスレッドを開始し、新しいスレッドで run() メソッドを実行します。
Mythread.run() は現在のスレッドで run() メソッドを直接実行し、run() を実行するための新しいスレッドを開始しません。

2. start() と run() の違いの例
以下では、簡単な例を使用してそれらの違いを示します。ソースコードは次のとおりです。

// 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();
  }
}
ログイン後にコピー

実行結果:

main call mythread.run()
main is running
main call mythread.start()
mythread is running
ログイン後にコピー

結果の説明:
(1) Thread.currentThread().getName() は、「」の名前を取得するために使用されます。現在のスレッド」。現在のスレッドは、CPU で実行がスケジュールされているスレッドを指します。
(2) mythread.run() は「メインスレッド main」で呼び出され、run() メソッドは「メインスレッド main」上で直接実行されます。
(3) mythread.start() は「スレッド mythread」を開始します。「スレッド mythread」が開始された後、run() メソッドが呼び出されます。この時点では、run() メソッドは「スレッド」上で実行されます。神話」。

スレッドの中断と終了

1. スレッドの中断: このスレッドを中断するには、interrupt()
interrupt() を使用します。
このスレッドはそれ自体に割り込むことができます。他のスレッドがこのスレッドのinterrupt()メソッドを呼び出すと、checkAccess()を通じて権限がチェックされます。これにより、SecurityException がスローされる場合があります。
このスレッドがブロック状態にある場合: スレッドの wait()、wait(long)、または wait(long, int) を呼び出すと、スレッドは待機 (ブロック) 状態になります。または、スレッドの join()、join(long) を呼び出すと、スレッドは待機 (ブロック) 状態になります。 )、join(long, int)、sleep(long)、sleep(long, int) もブロック状態にします。スレッドがブロックされているときにそのスレッドがその中断() メソッドを呼び出すと、その「中断ステータス」がクリアされ、InterruptedException が受信されます。たとえば、スレッドは wait() によってブロック状態に入り、interrupt() によってスレッドを中断します。interrupt() を呼び出すとすぐにスレッドの割り込みフラグが「true」に設定されますが、スレッドはブロック状態にあるため、 「割り込みフラグ」は即座に「false」にクリアされ、同時にInterruptedExceptionが発生します。
セレクターでスレッドがブロックされている場合、interrupt(); によって中断されると、スレッドの割り込みフラグが true に設定され、選択操作からすぐに戻ります。
上記の状況に該当しない場合、スレッドがinterrupt()を通じて中断されると、その中断フラグは「true」に設定されます。
「終了したスレッド」を中断しても何も起こりません。

2. スレッドの終了
Thread の stop() メソッドと submit() メソッドは、本質的に安全性が低いため、使用しないことをお勧めします。
以下では、まず「ブロック状態」と「実行状態」それぞれのスレッドの終了方法について説明し、その後一般的な方法をまとめます。
1.「ブロック状態」のスレッドを終了する
通常、「ブロック状態」のスレッドを「中断」により終了させます。
sleep()、wait()、join() などのメソッドの呼び出しによりスレッドがブロック状態になると、この時点でスレッドの中断() が呼び出される場合、スレッドの割り込みフラグは true に設定されます。ブロック状態であるため、割り込みマークがクリアされ、InterruptedException が発生します。スレッドは、適切なまで InterruptedException を配置することで終了できます。形式は次のとおりです。

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

説明: スレッドがブロックされているときに、スレッドの中断() を呼び出すと、InterruptedException 割り込みが生成されます。 。割り込みは while(true) の外側でキャプチャされるため、while(true) ループを終了します。
注: InterruptedException のキャプチャは通常、while(true) ループ本体の外側に配置されるため、例外が発生したときに while(true) ループが終了します。それ以外の場合、InterruptedException が while(true) ループ本体内にある場合は、追加の終了処理を追加する必要があります。形式は次のとおりです。

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

説明: 上記の InterruptedException 例外は whle(true) 内でキャプチャされます。 InterruptedException 例外が発生した場合、その例外は catch によって処理される以外の while(true) ループ本体内にあります。while(true) ループ本体を終了するには、while(true) を終了するための追加の操作が必要です。
2.「実行状態」のスレッドを終了する
通常、「実行状態」のスレッドを「マーク」で終了します。その中には「中断マーク」と「追加加算マーク」が含まれます。
(1) 「割り込みフラグ」によりスレッドを終了します。
フォームは次のとおりです:

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


関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート