首页 > 其他分享 >聊一聊如何优雅的停下线程,除了这两种还有其他的吗?

聊一聊如何优雅的停下线程,除了这两种还有其他的吗?

时间:2022-10-29 15:04:38浏览次数:79  
标签:product Thread InterruptedException 优雅 聊一聊 线程 new public

聊一聊如何优雅的停下线程,除了这两种还有其他的吗?_ide

前言

今天主要来聊一聊如何优雅的停下线程。

在开始之前,我们可以思考一下,如何能够让线程停下?

通过查阅JDK,我们不难发现Thread为我们提供了一个stop方法,只要使用stop方法,就立即停止线程,但是发现stop()方法被标注为废弃的方法,因为这个方法会强行把执行到一半的线程终止,可能会引发一些数据不一致或者我们没发预估的问题。

除了stop()方法,我能想到的方案还有两个,

方案一:使用volatile标记位,利用其可见性

方案二:调用Thread的方法interrupted

方案实现

方案一:使用volatile标记位,利用其可见性

通过代码我们来看下方案一,这是一个很经典的生产者和消费者模式。

生产者Demo

//生产者
class Producer implements Runnable {
public volatile boolean canc = false;

private Product product;

Producer(Product product) {
this.product = product;
}

@Override
public void run() {
try {
while (!canc) {
try {
//Thread.sleep(1000);
product.put("iphone6s");
System.out.println("put:" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (Exception ex) {
...
} finally {
System.out.println("结束");
}
}
}
复制代码

消费者Demo

//消费者
class Consumer implements Runnable{
private Product product;

Consumer(Product product) {
this.product = product;
}

@Override
public void run() {
while (Math.random() > 0.9){
try {
Thread.sleep(1000);
product.take("iPhone6s");
System.out.println("take:"+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
复制代码

调用生产者和消费者

public static void main(String[] args) {
ArrayBlockingQueue queue = new ArrayBlockingQueue(1);
Product product = new Product(queue);
Producer producer = new Producer(product);
Consumer consumer = new Consumer(product);
Thread c1 = new Thread(consumer);
Thread p1 = new Thread(producer);
p1.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
c1.start();
System.out.println("消费者不需要更多数据了。");
producer.canc = true;
System.out.println(producer.canc);
System.out.println(p1.getState());
}
复制代码

场景一: 我们把消费者和生产者的线程都开起来,生产者生产一个产品,消费者都会消费一个产品,这个时候volatile的值,在下一次的轮询中值已经变成了true,就跳出while循环,线程就停止,这个场景下volatile就适用了。

场景二: 我们将消费者线程不启动,只生产不消费。 理论上我们期待的结果应该也是值变成true,跳出while循环,线程停止。

结果打印:

Put a iphone6s
put:Thread-2
消费者不需要更多数据了。
valatile的值: true
线程状态:WAITING
复制代码

根据打印的结果我们会观察到他没有输出结束的语句,

我们看到了生产者生产了产品,valatile也修改了值,但是线程却没有结束,

这主要的原因是因为,生产者执行了product.put("iphone6s"),没有被消费,造成了阻塞,在它唤醒之前,

无法进入下一次的轮询判断。造成了值修改了,却没有做出相应处理。

我们发现在消费的时候,take方法内部会触发唤醒,当检测到线程已经停止,则抛出InterruptedException异常。

开源码说话,可以看到dequeue,唤醒了线程。

public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}

public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
}

private E dequeue() {
...
//释放
notFull.signal();
return x;
}
复制代码

二、方案二:调用Thread的方法interrupted

static class CreateRunable implements Runnable {
public CreateRunable(int i) {
this.i = i;
}

private int i;

public int getI() {
return i;
}

public void setI(int i) {
this.i = i;
}

@Override
public void run() {
synchronized (this){
while ( !Thread.currentThread().isInterrupted() ){
System.out.println("Runable接口,实现线程"+i++);
}
}
}
}
复制代码
Thread createThread = new Thread(new CreateRunable(0));
createThread.start();
Thread.sleep(5);
createThread.interrupt();
复制代码

休眠5毫秒后,该线程检查到了中断信号,就会停止线程。

那如果任务正在休眠状态,线程会如何处理呢

@Override
public void run() {
synchronized (this){
while ( !Thread.currentThread().isInterrupted() ){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Runable接口,实现线程"+i++);
}
}
}
复制代码

抛出异常,同时清除中断状态,线程会继续执行

Runable接口,实现线程0
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at main.Thread.threadStartThreeWays$CreateRunable.run(threadStartThreeWays.java:48)
at java.lang.Thread.run(Thread.java:748)
Runable接口,实现线程1
Runable接口,实现线程2
Runable接口,实现线程3
复制代码

总结

我们在这里就不说Stop()方法,因为他太暴力了,不够优雅。这里的优雅指的是可以让线程有时间做好收尾工作,避免数据的错乱。 优雅停下线程的方式主要有两种

  • 方案一:使用volatile标记位。
  • 方案二:调用Thread的方法interrupted。

通过上面的demo案例,我们可以看到使用方案一的volatile,在某一些特殊的场景下,会发生不能关闭线程的情况。

所以volatile是不够全面的。方案二则是一种更优的选择。

标签:product,Thread,InterruptedException,优雅,聊一聊,线程,new,public
From: https://blog.51cto.com/u_15773567/5806069

相关文章

  • Linux--多线程(一)
    线程线程的概念线程:线程是OS能够进行运算调度的基本单位。线程是一个进程中的一个单一执行流,通俗地说,一个程序里的一个执行路线就叫做线程。可以知道的是,一个进程至少......
  • java-实时打印当前线程的调用堆栈
    java-实时打印当前线程的调用堆栈/***打印当前线程的调用堆栈**/voidprintTrack(){StackTraceElement[]st=Thread.currentThre......
  • C#使用线程和线程同步
    操作系统由多个进程的运行来维持的,进程又被细化为线程(Thread)。一个完整的进程拥有独立的内存空间,同一个进程内的线程是共享内存空间和数据。在C#中线程使用委托的方式来完......
  • 【简欧风格设计装修案例】华丽而又不失优雅,时尚优雅并存!
    ​现代简欧风格装修设计在国内现在已经相当普及了,既有欧式风格的奢华感,又有现代风格的流行风尚。这种时尚而富有内涵的风格正逐渐受到一些年轻人的追崇。 简欧风格是欧式装......
  • Java多线程(6):锁与AQS(上)
    您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来~ 在Java面试中,有一类高频问题会经常问到(火箭式问题):Java有几种锁?都是干嘛的?我想对于面试经验较为丰富的人,这个问题极有可......
  • 多进程与多线程 python
    进程之间的数据传递全局变量在多个进程中不共享,进程之间的数据是独立的,默认情况下互不影响用Queue实现多进程之间的数据传递Queue是多进程安全的队列,可以使用Queue......
  • 多线程
    学习文档:https://www.cnblogs.com/gh110/p/15153666.html学习视频:https://www.bilibili.com/video/BV1V4411p7EF/ 1.线程创建(三种方法)1.1继承Thread类(重要)a.自定......
  • Java多线程(5):CAS
    您好,我是湘王,这是我的51CTO博客,欢迎您来,欢迎您再来~​在JDK1.5之前,Java的多线程都是靠synchronized来保证同步的,这会引起很多性能问题,例如死锁。但随着Java的不断完善,JNI(Java......
  • C# tcp 多线程服务端和客户端
    主要是服务端usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Net;usingSystem.Net.Sockets;usingSystem.Text;usingSystem.Threading;n......
  • 如何在uniapp中优雅地使用WebView
    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助从webview页面传值到uniapp中官方文档已经很详细了,这里给大家上我的实战代码,首先在webview页面中引入相......