volatile 可以线程中断的demo
主线程让子线程运行5s后进行中断的代码如下.
使用volatile标记布尔值 .
5s后, 把布尔值标记为true代表 发出中断的信号
/**
* 类名称:WrongwayVolatile
* 类描述:volatile 看似可以进行中断的demo
* 主线程让子线程运行5s后进行中断
*
* @author: https://javaweixin6.blog.csdn.net/
* 创建时间:2020/8/27 7:40
* Version 1.0
*/
public class WrongwayVolatile implements Runnable {
private volatile boolean canceled = false;
@Override
public void run() {
int num = 0;
try {
while (num <= 1000000 && !canceled) {
if (num % 100 == 0) {
System.out.println(num+" 是100的倍数");
}
num++;
Thread.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
WrongwayVolatile r = new WrongwayVolatile();
Thread thread = new Thread(r);
thread.start();
//主线程停止5s . 让子线程运行5s
Thread.sleep(5000);
//把canceled标记为true, 子线程中用 !canceled 取反 中断线程
r.canceled = true;
}
}
控制台打印如下 , 5s后, 子线程成功的中断 , 停止运行.
volatile 无法中断线程的demo
使用生产者 消费者的例子, 演示 volatile无法停止线程 .
一旦消费者不需要消费更多的数据了, 那么应该让生产者也停下来.
把volatile 修饰的 布尔变量改成了true.
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* 类名称:WrongWayVolatileCantStop
* 类描述: 演示volatile无法停止线程
* 当线程进入阻塞时, volatile是无法停止线程的
* <p>
* 生产者生产的速度很快, 消费者的消费速度慢,
* 所以阻塞队列满了之后, 生产者会阻塞, 等待消费者进一步消费
*
* @author: https://javaweixin6.blog.csdn.net/
* 创建时间:2020/8/27 7:48
* Version 1.0
*/
public class WrongWayVolatileCantStop {
public static void main(String[] args) throws InterruptedException {
WrongWayVolatileCantStop body = new WrongWayVolatileCantStop();
//仓库 阻塞队列, 满了的时候放不进去 空了的时候再取数据也会阻塞
ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
//生产者实例作为runnable的对象. 由于main方法为static静态方法, 因此要用对象主类对象.内部类创建对象
Producer producer = body.new Producer(storage);
//创建生产者线程
Thread producerThread = new Thread(producer);
producerThread.start();
//让生产者运行1s, 放数据到 BlockingQueue 中
Thread.sleep(1000);
//创建消费者
Consumer consumer = body.new Consumer(storage);
while (consumer.needMoreNums()) {
System.out.println(consumer.storage.take()+" 被消费了 ");
//每次消费100ms
Thread.sleep(100);
}
//一旦跳出了while循环, 则打印消费者不需要更多数据
System.out.println("消费者不需要更多数据了 ...... ");
//一旦消费者不需要消费更多的数据了, 那么应该让生产者也停下来.
producer.canceled=true;
System.out.println(producer.canceled);
}
//生产者
class Producer implements Runnable {
private volatile boolean canceled = false;
//生产者的阻塞队列
BlockingQueue stotage;
public Producer(BlockingQueue stotage) {
this.stotage = stotage;
}
@Override
public void run() {
int num = 0;
try {
while (num <= 1000000 && !canceled) {
//如果当前数是100的倍数, 那么放入仓库
if (num % 100 == 0) {
stotage.put(num);
System.out.println(num + " 是100的倍数 , 放入仓库中");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生产者停止运行 ");
}
}
}
//消费者
class Consumer {
BlockingQueue storage;
public Consumer(BlockingQueue storage) {
this.storage = storage;
}
//判断是否需要更多的消费资源
public boolean needMoreNums(){
//判断是否大于0.95
if (Math.random() > 0.95) {
return false;
}
return true;
}
}
}
但实际的运行效果如下 . 控制台打印了把标记位改成了true, 但是可以看到程序依然在后台运行中.
volatile无法中断线程的错误原因分析
在面对线程遇到长时间阻塞的情况的时候, 是没有办法及时唤醒线程的
主要是代码阻塞在了第75 行 , 当BlockingQueue 的put满了的时候, 是不会往下走的.
那么也就不会走到第70行代码, 去检查, 线程是否被停止了.
此种情况, Java的设计者也考虑过了, 因此才用了Interrupt去停止线程 , 而不是推荐用 volatile去停止线程
修复停止线程的错误
如下的代码为对上一节 volatile无法中断线程 的修复.
主要是取代了使用volatile 修饰的布尔变量作为是否停止线程的标记, 而是使用了interrupt 方法来发出中断线程的信号
package com.thread.stopthreads.volatiledemo;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* 类名称:WrongWayVolatileCantStop
* 类描述: 演示volatile无法停止线程
* 当线程进入阻塞时, volatile是无法停止线程的
* <p>
* 生产者生产的速度很快, 消费者的消费速度慢,
* 所以阻塞队列满了之后, 生产者会阻塞, 等待消费者进一步消费
*
* @author: https://javaweixin6.blog.csdn.net/
* 创建时间:2020/8/27 7:48
* Version 1.0
*/
public class WrongWayVolatileFixed {
public static void main(String[] args) throws InterruptedException {
WrongWayVolatileFixed body = new WrongWayVolatileFixed();
//仓库 阻塞队列, 满了的时候放不进去 空了的时候再取数据也会阻塞
ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
//生产者实例作为runnable的对象. 由于main方法为static静态方法, 因此要用对象主类对象.内部类创建对象
Producer producer = body.new Producer(storage);
//创建生产者线程
Thread producerThread = new Thread(producer);
producerThread.start();
//让生产者运行1s, 放数据到 BlockingQueue 中
Thread.sleep(1000);
//创建消费者
Consumer consumer = body.new Consumer(storage);
while (consumer.needMoreNums()) {
System.out.println(consumer.storage.take()+" 被消费了 ");
//每次消费100ms
Thread.sleep(100);
}
//一旦跳出了while循环, 则打印消费者不需要更多数据
System.out.println("消费者不需要更多数据了 ...... ");
//一旦消费者不需要消费更多的数据了, 那么应该让生产者也停下来.
// producer.canceled=true;
//使用interrupt 给出中断信号
producerThread.interrupt();
}
//生产者
class Producer implements Runnable {
//生产者的阻塞队列
BlockingQueue stotage;
public Producer(BlockingQueue stotage) {
this.stotage = stotage;
}
@Override
public void run() {
int num = 0;
try {
//如果生产者没有被中断, 则继续执行
while (num <= 1000000 && !Thread.currentThread().isInterrupted()) {
//如果当前数是100的倍数, 那么放入仓库
if (num % 100 == 0) {
System.out.println("进入阻塞 ");
stotage.put(num);
System.out.println(num + " 是100的倍数 , 放入仓库中");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生产者停止运行 ");
}
}
}
//消费者
class Consumer {
BlockingQueue storage;
public Consumer(BlockingQueue storage) {
this.storage = storage;
}
//判断是否需要更多的消费资源
public boolean needMoreNums(){
//判断是否大于0.95
if (Math.random() > 0.95) {
return false;
}
return true;
}
}
}
main方法中 ,发出中断线程的信号
子线程中, 判断是否被中断了.
控制台打印如下, 可以看到成功的中断了线程