java线程挂掉_Java线程停止的坑

复杂点的Java应用一般都会开多个线程在后台跑程序,有时候因为发布或其他原因需要停止程序,那就涉及到如何停止Java线程的问题.Java 的线程停止还是比较别扭的,如果是从其他语言转学java,很容易掉进坑里面.

一 坑一 利用线程的stop方法

如果初学Java,在看到Thread类有个stop方法,那第一反应停止应该用stop吧。在好些的IDE里面会给你提示,此方法已经被标记为:@Deprecated,为什么那。那是因为stop是直接停止线程,为什么直接停止程序不行那,因程序可能还没有处理完内存中积压的数据,直接强制停止,就可能会造成数据的完整性被破坏,和我们尽量不要用kill -9 pid来这种暴力停止程序的原因一样。

那就继续找:suspend和resume 看到这两个方法,将线程暂停的时候,也许会用到此方法。结果这个也是坑,这两个方法同样被标记为:@Deprecated,为什么这种暂停的方法也不建议用那,这是因为java的线程在调用suspend暂停后,并没有释放所持有的锁,这样如果其他线程刚好必须用到这把锁,迟迟获取不到,就可能进入到死锁状态。

二 坑二利用volatile 标记位

这个是从其他语言转Java的人常用的一种停止线程的方法,这种方法我以前经常用,觉得简单方便,第一次看到用这种方法停止线程是错误的,真令我惊讶。当然不是所有的情况下,都会有问题,在特定的情况下会线程会无法终止。

实例代码如下:

生产者代码:

class Producer implements Runnable {

public volatile boolean canceled = false;

BlockingQueue storage;

public Producer(BlockingQueue storage) {

this.storage = storage;

}

@Override

public void run() {

int num = 0;

try {

while (num <= 100000 && !canceled) {

if (num % 30 == 0) {

storage.put(num);

System.out.println(num + "放入队列中。");

}

num++;

}

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

System.out.println("生产者结束运行");

}

}

}

消费者代码:

import java.util.concurrent.BlockingQueue;

class Consumer {

BlockingQueue storage;

public Consumer(BlockingQueue storage) {

this.storage = storage;

}

public boolean needMoreNums() {

if (Math.random() > 0.89) {

return false;

}

return true;

}

}

main函数代码:

import java.util.concurrent.ArrayBlockingQueue;

public class TestStopThread

{

public static void main(String[] args) throws InterruptedException {

ArrayBlockingQueue storage = new ArrayBlockingQueue(8);

Producer producer = new Producer(storage);

Thread producerThread = new Thread(producer);

producerThread.start();

Thread.sleep(800);

Consumer consumer = new Consumer(storage);

while (consumer.needMoreNums()) {

System.out.println(consumer.storage.take() + "被消费了");

Thread.sleep(100);

}

System.out.println("消费者停止。");

// 停止标识设置为true 停止生产者

producer.canceled = true;

System.out.println(producer.canceled);

}

}

代码比较简单,生产者和消费者通过ArrayBlockingQueue 队列相连,生产者生产30倍数的数字,消费者消费,消费者每消费一个数字就会取个随机数看下是否大于0.89,大于则消费者停止消费。 主线程设置生产者停止标识,标识生产者可以停止了,停止后,整个程序也应该停止了。

运行结果:

af8f702773ce8d387f77ec16c3f2d70f.png

从结果可以看出,虽然生产者的停止标识为true了,但是整个程序仍然没有停止。

这是因为在生产者的循环中,storage.put(num); 队列满了之后,发生了阻塞,生产者的线程就阻塞在这里面,虽然canceled标识已经设置为true了,但是程序还没有到判断循环标志着一步,所以一直是卡着的。

三线程停止的正确方式

Java的线程停止的正确姿势,是采用中断方式,把生产者的核心代码改动如下:

@Override

public void run() {

int num = 0;

try {

while (num <= 100000 && !   Thread.currentThread().isInterrupted()) {

if (num % 30 == 0) {

storage.put(num);

System.out.println(num + "倍数放入队列中。");

}

num++;

}

} catch (InterruptedException e) {

e.printStackTrace();

System.out.println("线程检测到中断信号");

} finally {

System.out.println("生产者结束运行");

}

}

主函数改成通过中断方式来通知生产者线程停止:

producerThread.interrupt();

运行结果:

52fa1bba4a2d3778500d2faacc7ddf9c.png

可以看到中断可以打断线程的阻塞,让线程继续执行下去,而且如果线程处于sleep状态,中断仍然可以打断sleep状态,这样就不用像刚才的线程标识那样,还必须等待线程休眠完毕后才可以响应。

中断发生后,我们还可以继续处理剩下的数据,这样就不像stop()方法那样处理的太生硬,而导致数据不一致问题。

isInterrupted() 会检测中断标识,检测到返回true,并将标志设置成false。所以我们在编写多线程的处理任务代码的时候,不要将中断吞并而不处理,特别有些人喜欢用catch(Execption e)方式来捕获异常,而不处理。如果框架采用这种中断方式来停止线程,那就无法停止线程。

实话实说,我觉得java这种停线程的方式很容易让人误用,没办法,就这样设计的。

四 诗词欣赏

浣溪沙·山色横侵蘸晕霞

[宋代] [苏轼]

山色横侵蘸晕霞,湘川风静吐寒花。远林屋散尚啼鸦。

梦到故园多少路,酒醒南望隔天涯。月明千里照平沙。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值