停止线程的正确姿势
- 说明
- 错误的停止方式
- 停止线程的正确姿势
- 无法响应中断时如何停止线程
- 总结
说明
可能很多小伙伴对线程停止是一个模糊的概念,甚至误用了一些错误的停止方式,如果在开发中将造成不可设想的后果,学好线程是进阶的一大途径,本文将为你讲解如何错误的停止方法以及正确的停止线程
错误的停止方式
1.suspend()
官方已经说明废弃,因为该方法线程带着锁去睡眠的,可想而知,效率非常低,以及非常容易造成死锁
代码演示:
public class SuspendTest{
public static void main(String[] args) throws InterruptedException {
SuspendTest suspendTest = new SuspendTest();
ThreadTest threadTest = suspendTest.new ThreadTest();
Thread thread1 = new Thread(threadTest);
thread1.start();
// 如果目标线程(thread1)在挂起时保护关键系统资源的监视器上的锁定,则在目标线程恢复之前,线程(thread2)不能访问该资源。
//如果要恢复目标线程的线程在调用resume之前尝试锁定此监视器, resume导致死锁。
thread1.suspend();
Thread thread2 = new Thread(threadTest);
thread2.start();
// Thread.sleep(3000);
//唤醒线程
// thread1.resume();
// thread2.resume();
}
class ThreadTest implements Runnable{
@Override
public void run() {
System .out.println(Thread.currentThread().getName()+"执行了Run方法");
}
}
}
一直带着锁阻塞 直到resume唤醒,下一个线程才能访问资源
2.stop()
同样是被官方弃用,线程停止,我们要有一个概念,我们不能立即关闭中断线程,这样非常不安全,容易造成数据遗失,垃圾机制不能正常回收,就好比我们的电脑突然关机一样,这是一种不好的思想
3.通过Boolean值停止线程
这里相信很多朋友都应该使用过这种方式去停止线程,但是这种方式在一些情况下是不可取的,有些小伙伴们就说,我明明用的可好了,那我们用代码演示来说话
1.首先我们创建一个生产者和消费者
2.生产者生产速度比较快,当队列满时,进行阻塞,等待消费者消费
3.消费者需要一定时间进行消费
4.结束消费者的同时,结束生产者
/**
* @Description: volatile Boolean 标识是一种错误的线程停止方法
* @Author: songbiao
*/
public class VolatileErrorTest {
public static void main(String[] args) throws InterruptedException {
//实例化内部类需要先实例化外部类
VolatileErrorTest test = new VolatileErrorTest();
//阻塞队列 大小超过10会阻塞
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(10);
//创建生产者
Producer producer = test.new Producer(blockingQueue);
//创建消费者
Customer customer = test.new Customer(blockingQueue);
//创建线程
Thread thread = new Thread(producer);
Thread thread1 = new Thread(customer);
//启动线程
thread.start();
thread1.start();
//睡眠一秒
Thread.sleep(1000);
//向消费者发送中断通知
thread1.interrupt();
//使用Boolean来尝试结束生产者 (不可取)
producer.cancel = true;
}
//生产者
class Producer implements Runnable{
//用来判断是否结束
Boolean cancel = false;
ArrayBlockingQueue blockingQueue;
public Producer(ArrayBlockingQueue blockingQueue) {
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
try {
//满足条件就执行
for (int i = 0; i < 1000 && !cancel; i++) {
//我们写一个小小的逻辑
if (i % 2 != 0) {
//进行生产
blockingQueue.put(i);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//执行结束会打印
System.out.println("生产者 生产结束..");
}
}
}
//消费者
class Customer implements Runnable {
ArrayBlockingQueue blockingQueue;
public Customer(ArrayBlockingQueue blockingQueue) {
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
try {
while (true) {
//消费者消费所需时间
Thread.sleep(100);
//消费生产
System.out.println(blockingQueue.poll());
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//执行结束会打印
System.out.println("消费者 消费结束...");
}
}
}
}
执行结果,我们发现生产者并没有结束,程序依旧还在运行
分析: 由此可见这并不是一种很好的停止线程方式
for (int i = 0; i < 1000 && !cancel; i++) {
if (i % 2 != 0) {
// 当数量超过10 会队列阻塞 无法去进去条件判断 无线等待
blockingQueue.put(i);
}
}
停止线程的正确姿势
1.传递中断通知
@Override
public void run() {
//每次循环都会判断当前线程是否需要中断
for (int i = 0; i < Integer.MAX_VALUE && !Thread.currentThread().isInterrupted(); i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
}
public static void main(String[] args) throws InterruptedException {
InterruptStopTest interruptStopTest = new InterruptStopTest();
Thread thread = new Thread(interruptStopTest);
//启动线程
thread.start();
//睡眠10毫秒
Thread.sleep(10);
//传递中断通知
thread.interrupt();
}
执行结果,到1466的时候就接受到了中断通知,跳出循环
2.响应中断
同样是使用interrup方法来中断
@Override
public void run() {
try {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
if (i % 2 != 0) {
//sleep响应中断 抛出异常
Thread.sleep(20);
System.out.println(i);
}
}
} catch (InterruptedException e) {
//注意: sleep方法会清除中断通知
System.out.println(Thread.currentThread().isInterrupted());
System.out.println("线程执行结束");
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
InterruptStopTest02 test02 = new InterruptStopTest02();
Thread thread = new Thread(test02);
thread.start();
Thread.sleep(2000);
//传递中断通知
thread.interrupt();
}
执行结果,正确停止
无法响应中断时如何停止线程
如果线程阻塞是由于调用了 wait(),sleep() 或 join() 方法,你可以中断线程,通过抛 出
InterruptedException 异常来唤醒该线程。
但是对于不能响应InterruptedException的阻塞,很遗憾,并没有一个通用的解决方 案。
但是我们可以利用特定的其它的可以响应中断的方法,比如
ReentrantLock.lockInterruptibly(),比如关闭套接字使线程立即返回等方法来达到目的。
答案有很多种,因为有很多原因会造成线程阻塞,所以针对不同情况,唤起的方法也不同。
总结就是说如果不支持响应中断,就要用特定方法来唤起,没有万能药
总结
停止线程的最优方式就是通过interrupt方法传递线程响应中断,然后在run方法中进行中断处理,需要两个线程之间的配合,
如果你看完了这篇文章,请试着纠正文章中的第三个错误停止案例,那么代表你已经掌握了
本文中都是通过实现Runnable接口来创建启动线程,如果对此不是很理解的话 可以看我上一篇文章。