您的位置:首页 > 编程语言 > Java开发

Java 如何正确停止一个线程

2017-10-16 19:54 751 查看
自己在做实验性小项目的时候,发现自己遇到一个问题:如何控制线程的"死亡"?

首先,如何开启一个线程呢?

最简单的代码:

1 public class Main {
2
3     public static void main(String[] args) {
4
5         Thread thread = new Thread(new Runnable() {
6             @Override
7             public void run() {
8                 System.out.println("当前线程:" + Thread.currentThread() + ",当前时间戳:" + System.currentTimeMillis());
9             }
10         });
11
12         thread.start();
13
14         try {
15             Thread.sleep(1000L);
16         } catch (InterruptedException e) {
17             e.printStackTrace();
18         }
19         System.out.println("主线程结束");
20     }
21 }


很简单,调用.start()方法,这个线程就会启动.

那么怎样主动去停止一个线程呢?要解答这个问题,首先要考虑:为什么要结束一个线程.

理由如下:

线程是JVM宝贵的资源,有的线程会长时间占用资源.

一些业务逻辑下会出现一个线程从逻辑上完全没有意义(比如一个定时器,调用了结束的方法),确实需要去停止.

结束一个线程有一个最基本的方法:Thread.stop()方法:

1 @Deprecated
2 public final void stop() {
3     SecurityManager security = System.getSecurityManager();
4     if (security != null) {
5         checkAccess();
6         if (this != Thread.currentThread()) {
7             security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
8         }
9     }
10     // A zero status value corresponds to "NEW", it can't change to
11     // not-NEW because we hold the lock.
12     if (threadStatus != 0) {
13         resume(); // Wake up thread if it was suspended; no-op otherwise
14     }
15
16     // The VM can handle all thread states
17     stop0(new ThreadDeath());
18 }


但是这个方法已经是:@Deprecated的了,也就是被建议不要使用的方法.

为什么不建议使用这个方法的官方说明: http://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html
实际上,我结合自己经验提出以下几点:

线程会直接停掉,按照代码逻辑要释放的资源,要调用的接口可能不会被运行(finally块的代码还是会执行)

会破坏锁,导致线程不安全(停掉一个线程就会释放它持有的锁,但不能保证逻辑上)

这两点都是非常严重的问题了.即使再小心,去调用stop()也会导致各种各样的问题.

如果不能"硬"实现结束线程,那么就可以考虑下"软"实现.

首先,导致一个线程长时间运行的原因无非有这么几个:

代码逻辑混乱\业务复杂,写了一个很长的run()方法

长时间睡眠

循环

对于这第一种嘛,只能从代码结构上优化了.

而这第二种,就要使用Thread.interrupt()方法了.这个方法唤醒睡眠中的线程:

1 public class Main {
2
3     public static void main(String[] args) {
4
5         Thread thread = new Thread(new Runnable() {
6             @Override
7             public void run() {
8                 try {
9                     TimeUnit.DAYS.sleep(1L);
10                     System.out.println("睡眠结束");
11                 } catch (Exception e) {
12                     System.out.println("异常:" + e);
13                 } finally {
14                     System.out.println("finally块被执行");
15                 }
16             }
17         });
18
19         thread.start();
20
21         if (!thread.isInterrupted()) {
22             thread.interrupt();
23         }
24         System.out.println("主线程结束");
25     }
26 }


在bio的服务端里面,会阻塞当前线程.监听的端口有消息,才会继续执行,而如果没有人连接,就需要通过这种方式来打断正在监听的线程.

对于这第三种,需要通过轮询标志位来控制退出.自己写的定时器代码:

1 public class TimerImpl implements Timer {
2
3     private static final Logger logger = LoggerFactory.getLogger(TimerImpl.class);
4
5     // 定时器线程
6     private TimerThread timerThread = null;
7
8     private volatile Boolean running = false;
9
10     private Handler taskHandler;
11
12     private Long time;
13
14     private TimeUnit unit;
15
16     @Override
17     public void start() throws Exception {
18
19         // 给参数生成本地固定拷贝,以确保在运行过程中,不会给参数的改变干扰
20         Handler curTask = taskHandler.getClass().newInstance();
21         Long curTime = new Long(time);
22         TimeUnit curUnit = unit.getClass().newInstance();
23
24         // 检查
25         if (ParameterUtil.checkNull(curTask, curTime, curUnit)) {
26             throw new Exception("定时器参数配置错误");
27         }
28
29         if (!running) {
30             synchronized (running) {
31                 if (!running) {
32                     timerThread = new TimerThread();
33
34                     timerThread.setTaskHandler(curTask);
35                     timerThread.setTime(curTime);
36                     timerThread.setUnit(curUnit);
37
38                     timerThread.start();
39                     running = true;
40                 }
41             }
42         }
43     }
44
45     @Override
46     public void stop() throws Exception {
47
48         if (!running) {
49             throw new Exception("定时器尚未开始");
50         }
51
52         synchronized (running) {
53             if (running) {
54                 // 标志位
55                 timerThread.cancel();
56                 // 打断睡眠
57                 if (!timerThread.isInterrupted()) {
58                     timerThread.interrupt();
59                 }
60                 running = false;
61             }
62         }
63     }
64
65     private class TimerThread extends Thread {
66
67         private volatile boolean stop = false;
68
69         private Handler taskHandler;
70
71         private Long time;
72
73         private TimeUnit unit;
74
75         @Override
76         public void run() {
77
78             // circle
79             while (!stop) {
80
81                 // sleep
82                 try {
83                     unit.sleep(time);
84                 } catch (InterruptedException e) {
85                     logger.info("定时线程被打断,退出定时任务");
86                     stop = true;
87                     return;
88                 }
89
90                 // do
91                 try {
92                     taskHandler.execute();
93                 } catch (Exception e) {
94                     logger.error("handler执行异常:{}", this.getClass(), e);
95                 }
96             }
97         }
98
99         public void cancel() {
100             stop = true;
101         }
102
103         public Handler getTaskHandler() {
104             return taskHandler;
105         }
106
107         public void setTaskHandler(Handler taskHandler) {
108             this.taskHandler = taskHandler;
109         }
110
111         public Long getTime() {
112             return time;
113         }
114
115         public void setTime(Long time) {
116             this.time = time;
117         }
118
119         public TimeUnit getUnit() {
120             return unit;
121         }
122
123         public void setUnit(TimeUnit unit) {
124             this.unit = unit;
125         }
126     }
127
128     @Override
129     public void setTimer(Long time, TimeUnit unit) {
130         this.time = time;
131         this.unit = unit;
132     }
133
134     @Override
135     public void setHandler(Handler handler) {
136         this.taskHandler = handler;
137     }
138
139 }


想要停止一个线程的方法是有的,但是会麻烦一些.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: