>  기사  >  Java  >  Java 다중 스레드 프로그래밍에서 스레드 시작, 중단 또는 종료에 대한 자세한 설명

Java 다중 스레드 프로그래밍에서 스레드 시작, 중단 또는 종료에 대한 자세한 설명

高洛峰
高洛峰원래의
2017-01-05 15:37:101427검색

스레드 시작:
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()는 "스레드 신화"를 시작합니다. "스레드 신화"가 시작된 후 run() 메서드가 호출됩니다. "스레드 신화".

스레드 중단 및 종료

1. 스레드 중단: Interrupt()
interrupt()의 기능은 이 스레드를 중단하는 것입니다.
이 스레드는 자신을 인터럽트할 수 있으며, 다른 스레드가 이 스레드의 Interrupt() 메서드를 호출하면 checkAccess()를 통해 권한을 확인합니다. 이로 인해 SecurityException이 발생할 수 있습니다.
이 스레드가 차단된 상태인 경우: 스레드의 wait(), wait(long) 또는 wait(long, int)를 호출하면 대기(차단) 상태로 들어가거나 스레드의 Join(), Join을 호출합니다. (long), Join(long, int), sleep(long), sleep(long, int)도 차단 상태로 전환합니다. 스레드가 차단된 동안 Interrupt() 메서드를 호출하면 "중단됨 상태"가 지워지고 InterruptedException이 수신됩니다. 예를 들어, 스레드는 wait()를 통해 차단 상태에 들어간 다음 Interrupt()를 통해 스레드를 중단합니다. Interrupt()를 호출하면 스레드의 인터럽트 플래그가 즉시 "true"로 설정되지만 스레드가 차단 상태에 있기 때문에 "인터럽트 플래그"는 즉시 "false"로 지워지고 동시에 InterruptedException이 생성됩니다.
스레드가 선택기에서 차단되고 인터럽트()에 의해 중단되면 스레드의 인터럽트 플래그가 true로 설정되고 선택 작업에서 즉시 반환됩니다.
위의 상황에 해당하지 않는 경우, 인터럽트()를 통해 스레드가 중단되면 해당 스레드의 인터럽트 플래그가 "true"로 설정됩니다.
"종료된 스레드"를 중단해도 아무 일도 일어나지 않습니다.

2. 스레드 종료
Thread의 stop() 및 suspens() 메서드는 본질적인 불안정성으로 인해 더 이상 사용하지 않는 것이 좋습니다!
아래에서는 먼저 "blocked 상태"와 "running 상태"에서의 스레드 종료 방법에 대해 논의한 후 일반적인 방법을 요약하겠습니다.
1. "차단된 상태"의 스레드를 종료합니다.
보통 "차단된 상태"의 스레드를 "중단"을 통해 종료합니다.
sleep(), wait(), Join() 등의 호출로 인해 스레드가 차단 상태에 들어갈 때, 이때 스레드의 Interrupt()가 호출되면 스레드의 인터럽트 플래그가 true로 설정됩니다. . 차단된 상태이기 때문에 인터럽트 표시가 지워지고 InterruptedException이 발생합니다. 적절할 때까지 InterruptedException을 배치하여 스레드를 종료할 수 있습니다. 형식은 다음과 같습니다.

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

설명: while(true)에서 작업을 계속 실행합니다. 스레드가 차단된 상태에 있을 때 스레드의 Interrupt()를 호출하면 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中文网!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.