您所在的位置:主页 > JAVA技术 >

Java多线程编程如何终止一个线程

时间:2015-08-29 09:48来源:未知 作者:os 点击:

  

  1. Thread.stop()函数

  stop()函数终止线程就像是强行拔掉电源线关机一样,可能会带来未知风险,因此目前不再推荐使用这种方式。请忘记它吧~~

  2. 改变标志变量状态

  通常我们会在线程中使用一个标志变量来控制线程的运行,如:

  [java]

  public class TestCallable implements Runnable {

  private boolean running = true;

  public void stop() {

  this.running = false;

  }

  public void run() {

  try {

  BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

  while (running) {

  System.out.println("线程正在运行中...");

  Thread.sleep(20000);

  }

  System.out.println("线程被终止.");

  } catch (IOException e) {

  e.printStackTrace();

  }

  }

  public static void main(String[] args) {

  try {

  TestCallable callable = new TestCallable();

  Thread th = new Thread(callable);

  th.start();

  Thread.sleep(1000);

  callable.stop();

  System.out.println("已下达终止线程命令。");

  } catch (Exception e) {

  e.printStackTrace();

  }

  }

  }

  执行上述代码就能发现,线程阻塞在reader.readLine()时,即使主线程改变了标志变量,但是并不能立即结束子线程,只有等待阻塞被打破,且运行到下一次循环条件判断的时候才能终止。所以在使用这种方法时,应该考虑到阻塞这种情况。当然,如果整个循环内的操作属于同一事务时,这种方法倒很不错。

  3. 中断函数interrupt()

  网上有很多论调说,终止线程的正确处理方式是使用interrupt中断,但真的是这样吗?实践出真知,拭目以待吧!

  如上2所述,如果线程中有Thread.sleep()阻塞时,改变标识变量无法达到终止线程的目的,那么此时可以使用Thread类的中断函数interrupt();

  如:

  [java]

  public class TestCallable extends Thread {

  BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

  public void stopThread() {

  interrupt();

  }

  public void run() {

  try {

  BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

  reader.close();

  while (!isInterrupted()) {

  System.out.println("线程正在运行中...");

  Thread.sleep(20000);

  }

  System.out.println("线程被终止.");

  } catch (IOException e) {

  e.printStackTrace();

  } catch (InterruptedException e) {

  e.printStackTrace();

  }

  }

  public static void main(String[] args) {

  try {

  TestCallable cal = new TestCallable();

  cal.start();

  Thread.sleep(2000);

  cal.stopThread();

  System.out.println("已下达终止线程命令。");

  } catch (Exception e) {

  e.printStackTrace();

  }

  }

  }

  调用线程对象的interrupt()时,sleep的线程会抛出InterruptedException异常,从而中断循环,终止线程。

  但是如果是IO如输入这些阻塞,中断的方法又不起作用了,还有就是对于没有阻塞的线程,调用interrupt()是达不到终止线程的效果的。

  那么,interrupt()中断在线程的哪些阻塞操作中能起到抛出InterruptedException作用呢?

  我们来做一个实验,代码如下:

  [java]

  public class TestCallable {

  public static void main(String[] args) {

  // 测试无阻塞的情况,进行中断

  Thread thread1 = new Thread(){

  public void run(){

  try {

  long time = System.currentTimeMillis();

  while(System.currentTimeMillis()-time<5000){

  }

  System.out.println("A1");

  } catch(Exception e) {

  System.out.println("B1");

  System.out.println("B1 : " + e.toString());

  }

  }

  };

  thread1.start();

  thread1.interrupt();

  //在线程sleep状态下进行中断

  Thread thread2 = new Thread(){

  public void run(){

  try {

  this.sleep(5000);

  System.out.println("A2");

  } catch (Exception e) {

  System.out.println("B2");

  System.out.println("B2 : " + e.toString());

  }

  }

  };

  thread2.start();

  thread2.interrupt();

  //在线程wait状态下进行中断,其中wait()没有在同步块中

  Thread thread3 = new Thread(){

  public void run(){

  try {

  this.wait(5000);

  System.out.println("A3");

  } catch (Exception e) {

  System.out.println("B3");

  System.out.println("B3 : " + e.toString());

  }

  }

  };

  thread3.start();

  thread3.interrupt();

  //在线程wait状态下进行中断,其中wait()在同步块中

  Thread thread4 = new Thread(){

  public void run(){

  try {

  synchronized(this){

  this.wait(5000);

  System.out.println("A4");}

  } catch (Exception e) {

  System.out.println("B4");

  System.out.println("B4 : " + e.toString());

  }

  }

  };

  thread4.start();

  thread4.interrupt();

  // join阻塞时进行中断

  Thread thread5 = new Thread() {

  public void run() {

  try {

  this.join(5000);

  System.out.println("A5");

  } catch (Exception e) {

  System.out.println("B5");

  System.out.println("B5 : " + e.toString());

  }

  }

  };

  thread5.start();

  thread5.interrupt();

  }

  }

  输出结果:

  [java]

  B2

  B4

  B4 : java.lang.InterruptedException

  B3

  B3 : java.lang.IllegalMonitorStateException

  B5

  B2 : java.lang.InterruptedException: sleep interrupted

  B5 : java.lang.InterruptedException

  A1

  结果分析:

  输出A1: 说明在无阻塞的时候,中断没有作用。

  输出B2 : java.lang.InterruptedException: sleep interrupted 说明sleep操作阻塞时,中断生效。

  输出B3 : java.lang.IllegalMonitorStateException 非中断引起的异常,这是wait()用法错误,这里就不详述。但正好可以看出,不论什么异常,都可以中断线程~

  输出B4: java.lang.InterruptedException 说明wait操作阻塞时,中断生效。

  输出B5:java.lang.InterruptedException 说明join操作阻塞时,中断生效。

  所以,针对线程的上述阻塞,都可以使用interrupted()方法中断线程,应该说,interrupted是对处于WAITTING 和TIME_WAITTING状态下的线程有用。

  那么问题来了,如果是其他阻塞情况呢?如IO阻塞,又该如何中断阻塞?其实从上面的例子可以大致得出结论:中断阻塞就可以。

  如:

  [java]

  public class TestCallable implements Runnable {

  BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

  public void stop() {

  try {

  reader.close();

  } catch (IOException e) {

  e.printStackTrace();

  }

  }

  public void run() {

  try {

  while (true) {

  System.out.println("线程开始执行...");

  String cmd = reader.readLine();

  }

  } catch (IOException e) {

  System.out.println("IO异常,线程结束。");

  e.printStackTrace();

  }

  }

  public static void main(String[] args) {

  TestCallable callable = new TestCallable();

  Thread th = new Thread(callable);

  th.start();

  callable.stop();

  }

  }

  通过关闭通道,抛出一个异常来中断阻塞,达到终止线程的目的。对于网络IO也是如此。

  4. 总结

  通过上述分析可知,要真正按我们的预期正确结束一个线程真的不容易!

  不过总得说来,终止线程有两类方法:

  针对没有阻塞的情况:设置标志变量,让线程正常自然死亡,和谐!

  针对有阻塞的情况:中断阻塞,靠抛出异常终止线程,看似暴力,但如果是我们预期的异常,那也是安全的!

  所以,要结合你自身程序的应用场景以及代码编写的具体情况,来确定最终方案。

  通过上述分析总结,我个人认为较好的方式是:

  (1)不管有没有阻塞情况,都用标志变量控制线程循环。

  网上有人使用如下的方式来代替标志变量,其实是不正确的,因为这样就依赖于线程的中断

  状态,可能导致线程非预期终止。

  [java]

  public void run() {

  while(!Thread.currentThread().isInterrupted()) {

  ...

  }

  }

  (2)用一个函数封装终止线程的操作,暴漏给外部调用。

  (3)针对线程中出现的阻塞情况,使用相应的中断方法,如线程的WATTING,TIME_WAITTING状态,可

  以用interrupted()方法,对于IO阻塞,可以关闭IO通道等。

  [java]

  public class TestCallable implements Runnable {

  // 不管是不是阻塞情况,都用标志变量控制线程循环

  private boolean running;

  BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

  /**

  * 用一个函数封装终止线程的操作

  */

  public void stop() {

  running = false;

  /*

  * 针对线程中出现的阻塞情况,使用相应的中断方法。

  * 如线程的WAITTING,TIME_WAITTING状态,可以用interrupted()

  * 对IO阻塞,可以关闭IO通道等。

  */

  try {

  reader.close();

  } catch (IOException e) {

  e.printStackTrace();

  }

  }

  public void run() {

  try {

  running = true;

  while (running) {

  System.out.println("线程开始执行...");

  String cmd = reader.readLine();

  }

  } catch (IOException e) {

  System.out.println("IO异常,线程结束。");

  e.printStackTrace();

  }

  }

  public static void main(String[] args) {

  TestCallable callable = new TestCallable();

  Thread th = new Thread(callable);

  th.start();

  callable.stop();

  }

  }

  5. 碎碎念

  本来只想总结一下终止线程,没想到又扯出了线程状态,这个就下回再详解吧。真是点带线,线带面啊,真真是“知识的海洋”......

  不过,收获不小,对于网络上得种种说法,进行试验、排错,最后得出自己的解法。如果您有更好的建议,热烈欢迎!!