zoukankan      html  css  js  c++  java
  • Java 如何正确停止一个线程

    自己在做实验性小项目的时候,发现自己遇到一个问题:如何控制线程的"死亡"?

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

    最简单的代码:

     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 }

    想要停止一个线程的方法是有的,但是会麻烦一些.

  • 相关阅读:
    python函数的基本语法<三>
    python函数的基本语法<二>
    python中文件的基础操作
    python模块——configparser
    python模块——psutil
    python中程序的异常处理
    python——协程
    hbuilder 开发app 自动升级
    C# datagridview 这是滚动条位置
    C# 小知识点记录
  • 原文地址:https://www.cnblogs.com/dsj2016/p/7678553.html
Copyright © 2011-2022 走看看