如何优雅地停下线程?除了这两种,你还能说出一种算我输
前言
今天主要来聊一聊如何优雅地停下线程。
在开始之前,我们可以思考一下,如何能够让线程停下?
通过查阅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的方法interruptedstatic 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是不够全面的。方案二则是一种更优的选择。
玉林又一网红打卡地火了!官方不要去近日,玉林又出现一个网红打卡地这个地方就是容县宁冲水库,在短视频平台上入围玉林市景点人气榜宁冲水库,是容县城区居民生活饮用水主要水源地,也是该县饮用水水源二级保护区。水库四周青山环
爱达邮轮cosma号开启处女航,迪拜港迎来202223年邮轮季11月17日,迪拜港迎来了来自爱达邮轮公司的AIDAcosma号,正式开始了202223年的邮轮季。AIDAcosma号是爱达船队中最大的邮轮之一,而且该船是爱达邮轮第一艘由液化天
合肥大圩火龙果红火上市来源人民网安徽频道nullnullnullnull连日来,合肥市包河区大圩镇的红心火龙果陆续成熟上市,果农抢抓时机采摘包装销售,同时也吸引了不少市民和游客前来采摘购买。近年来,大圩
莲池轶事古莲花池,原名雪香园,位于河北省保定市区中心,始建于元太祖二十二年(1227年)由于园内池塘中荷花茂盛,所以称为古莲花池。占地24000平方米,是中国十大历史名园之一。古莲花池是集
印象卢氏卢氏人心心念念的大天鹅,真的来了!初冬时节,暖阳高照,在洛河卢氏县范里镇苗村段,10余只白天鹅在此悠闲地游弋自由地觅食,与周边湖光山色融为一体,构成了一幅人与自然和谐共生的美丽画卷。近年来,卢氏县坚持生态立县三生融
新包LOEWE上架2023春夏系列男士新包明亮橘色,橡胶材质方形包新包LOEWE上架2023春夏系列男士新包明亮橘色,橡胶材质方形包LOEWE本周率先上市2023男士春夏系列作品,包袋以明快的橙橘色内敛的沥青灰色为主旋律,推出全新MoldedSl
这11部豆瓣8分的体育题材影视剧,哪个是你心中的NO。1?文叶秋臣因为本人非常喜欢篮球,所以看了不少相关领域的作品,比如定期重温的动画灌篮高手和空中大灌篮等等,甚至连台剧篮球火这种稍微沾点边儿的都追完了,单纯推荐篮球题材的话也能列出一个长
性感御姐姿态婀娜妩媚,端庄气质撩人心怀,袅袅气息淋漓绽放1前男友突然频繁地进入你的空间,请你别想太多,可能是他有个手欠的现女友。2每当男人说整理好了自己的房间时,标准通常指的是从门到床之间的路已经打通了。3许多伤害本来就是一次性的,可能
鉴道秦始皇雄才大略!胸怀天下秦始皇赢政史迹01少年继位秦始皇,赢政,秦庄襄王之子,战国时期秦国国君,秦王朝的建立者。即位时年仅13岁,吕不韦与太后宠信的宦官嫪毐专权用事。秦王政九年亲政后,镇压嫪毐叛乱。次年,
能在心中伴你一生,此生足矣红尘一世,总会遇见一个人,让你倾心。时光深处,总会有一份爱恋,难以忘怀。在我内心深处,住着一个我深深爱着的你,掩埋着一段回不去的曾经,在那个回不去的经年岁月里,我和你曾邂逅了这世间
人间烟火气最抚凡人心汪曾祺先生有一本书名为人间烟火最抚人心。甚是喜欢,于是就腆着脸化为己用。你我皆凡人,生在这人世间,终日奔波苦,一刻不得闲。然而,不管白天多么奔波多么辛苦,华灯初上,当回家后看到餐桌