并发编程学习笔记(三、如何安全地终止线程)

目录:

  • 设置退出标识
  • interrupt()方法
  • 废弃的终止方式
  • 总结

设置退出标识:

 1 public class FlagThread extends Thread {
 2     /**
 3      * 退出标识
 4      */
 5     public volatile boolean exit = false;
 6 
 7     @Override
 8     public void run() {
 9         while (!exit) {
10         }
11         System.out.println("ThreadFlag线程退出");
12     }
13 
14     public static void main(String[] args) throws Exception {
15         FlagThread threadFlag = new FlagThread();
16         threadFlag.start();
17         // 主线程延迟3秒
18         sleep(3000);
19         // todo 终止线程thread
20         threadFlag.exit = true;
21         // main线程放弃cpu使用权
22         // 让threadFlag线程继续执行,直到threadFlag运行完
23         threadFlag.join();
24         System.out.println("线程退出!");
25     }
26 }

通过第5行的exit属性来表示线程是否应该退出;但这种方法有一个弊端,若线程阻塞时标识便不起作用了。

在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是保证exit内存可见性,也就是对exit的修改会立刻对其他线程可见

interrupt()方法:

 1 public class InterruptThread extends Thread {
 2     /**
 3      * 退出标识
 4      */
 5     volatile boolean exit = false;
 6 
 7     @Override
 8     public void run() {
 9         while (!exit) {
10             System.out.println(getName() + " is running");
11             try {
12                 Thread.currentThread().join();
13             } catch (InterruptedException e) {
14                 System.out.println("week up from block...");
15                 // 在异常处理代码中修改共享变量的状态
16                 exit = true;
17             }
18         }
19         System.out.println(getName() + " is exiting...");
20     }
21 
22     public static void main(String[] args) throws InterruptedException {
23         InterruptThread interruptThread = new InterruptThread();
24         System.out.println("Starting thread...");
25         interruptThread.start();
26         Thread.sleep(3000);
27         System.out.println("Interrupt thread...: " + interruptThread.getName());
28         // 设置退出标识为true
29         interruptThread.exit = true;
30         interruptThread.interrupt();
31         // 主线程休眠3秒以便观察线程interruptThread的中断情况
32         Thread.sleep(3000);
33         System.out.println("Stopping application...");
34     }
35 }

若第30行注释,则在执行到第25行,运行到第12行时,线程一直在等待自己执行完,所以线程阻塞了,导致29行的标识无用。

此种情况只需要主动的中断线程即可(第30行代码)。

废弃的终止方式

1、Thread.stop:

  • Thread.stop()来强行终止线程,但是stop方法是很危险的,就像突然拔掉计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果。
  • Thread.stop()调用之后,创建子线程的线程就会抛出ThreadDeath这个Error强行释放子线程持有的锁,导致被保护的资源出现线程安全问题

2、Thread.suspend / Thread.resume:

  • Thread.suspend
    • 使线程暂停。
    • 不会释放类似锁这样的资源。
  • Thread.resume
    • 使线程恢复。
    • 如果之前没有使用suspend暂停线程,则不起作用
    • suspend()和resume()必须要成对出现,否则非常容易发生死锁。因为suspend方法并不会释放锁,如果使用suspend的目标线程对一个重要的系统资源持有锁,那么没任何线程可以使用这个资源,直到要suspend的目标线程被resumed,如果一个线程在resume目标线程之前尝试持有这个重要的系统资源锁再去resume目标线程,这两条线程就相互死锁了。

3、Runtime.runFinalizersOnExit:

  • 这个方法本身就是不安全的。
  • 它可能导致终结器(finalizers)被在活跃对象上被调用,而其他线程正在并发操作这些对象。而且,这个调用不是“线程安全”的,因为它设置了一个VM全局标志。
posted @ 2019-09-21 22:56  被猪附身的人  阅读(217)  评论(0编辑  收藏  举报