1. 被弃用的 stop、suspend 和 resume 方法
用 stop() 来停止线程,会导致线程运行一半突然停止,没办法完成一个基本单位的操作,会造成脏数据;
模拟连队发装备代码示例:
public class StopThread implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("连队" + i + " 开始领取:");
for (int j = 1; j <= 10; j++) {
System.out.println("士兵" + j + " 开始领取");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("====连队" + i + "领取完毕====");
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new StopThread());
thread.start();
// 等待666毫秒,停止线程
Thread.sleep(666);
thread.stop();
}
}
运行结果:
连队1 开始领取:
士兵1 开始领取
士兵2 开始领取
士兵3 开始领取
士兵4 开始领取
士兵5 开始领取
士兵6 开始领取
士兵7 开始领取
士兵8 开始领取
士兵9 开始领取
士兵10 开始领取
====连队1领取完毕====
连队2 开始领取:
士兵1 开始领取
士兵2 开始领取
士兵3 开始领取
Process finished with exit code 0
会发现 连队2 才3个人领完,其他人都还没有领到,线程突然就结束了,这样就会造成数据的错乱!
并且这种数据的错乱后期难以排查!
Oracle官方文档对于为什么Thread.stop不推荐使用的解释
https://docs.oracle.com/javase/7/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
suspend和resume被抛弃原因:
suspend和stop不一样,suspend不会破坏对象,但是它会让一个线程挂起,在恢复之前,锁不会释放,也就是它是带着锁去进行休息的,这样的话很容易造成死锁。
resume
2. 用volatile设置boolean标记位
2.1:看似可行的代码
public class WrongWayVolatile implements Runnable {
/**
* 设置boolean类型的标记位
*/
private volatile boolean canceled = false;
public static void main(String[] args) throws InterruptedException {
WrongWayVolatile r = new WrongWayVolatile();
Thread t = new Thread(r);
t.start();
// 等待5秒后
Thread.sleep(5000);
// 更改canceled值以达到停止程序的目的
r.canceled = true;
}
@Override
public void run() {
int num = 0;
try {
while (num <= Integer.MAX_VALUE && !canceled) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
Thread.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
程序在运行5秒钟后停止了,达到了预期的效果。
2.2:当陷入阻塞时,无法停止
下面代码示例中,生产者的生产速度很快,消费者消费速度慢,
所以阻塞队列满了以后,生产者会阻塞停止生产,等待消费者进一步消费
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class WrongWayVolatileCantStop {
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
Producer producer = new Producer(storage);
Thread producerThread = new Thread(producer);
producerThread.start();
// 等待1秒让生产者将队列填满
Thread.sleep(1000);
Consumer consumer = new Consumer(storage);
while (consumer.needMoreNums()) {
System.out.println(consumer.storage.take() + "被消费了!");
// 消费是需要时间的,设置个100毫秒
Thread.sleep(100);
}
System.out.println("消费者不需要更多数据了。");
// 一旦消费者不需要更多数据了,则应该让生产者停下来,
// 将标记位设置为true,看是否能将线程停止?
producer.canceled = true;
System.out.println(producer.canceled);
}
}
/**
* 生产者
*/
class Producer implements Runnable {
/**
* 设置boolean类型的标记位
*/
public volatile boolean canceled = false;
BlockingQueue storage;
public Producer(BlockingQueue storage) {
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
while (num <= Integer.MAX_VALUE && !canceled) {
if (num % 100 == 0) {
storage.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() {
// 随机返回true或false
if (Math.random() > 0.95) {
return false;
}
return true;
}
}
最后的运行结果并没有打印出 “生产者结束运行”,
而且程序也没有停止!!!
为什么?
因为生产者暂停生产时,是阻塞在 storage.put(num);
而且也没有人去唤醒,所以 while() 条件也无法执行,也不知道 canceled 的值已经被改变!
只会一直等在 storage.put(num); 这里。
解决方法:使用 interrupt
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class WrongWayVolatileCantStop {
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
Producer producer = new Producer(storage);
Thread producerThread = new Thread(producer);
producerThread.start();
// 等待1秒让生产者将队列填满
Thread.sleep(1000);
Consumer consumer = new Consumer(storage);
while (consumer.needMoreNums()) {
System.out.println(consumer.storage.take() + "被消费了!");
// 消费是需要时间的,设置个100毫秒
Thread.sleep(100);
}
System.out.println("消费者不需要更多数据了。");
// 一旦消费者不需要更多数据了,则应该让生产者停下来,
// 使用interrupt通知停止线程
producerThread.interrupt();
}
}
/**
* 生产者
*/
class Producer implements Runnable {
BlockingQueue storage;
public Producer(BlockingQueue storage) {
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
while (num <= Integer.MAX_VALUE && !Thread.currentThread().isInterrupted()) {
if (num % 100 == 0) {
storage.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() {
// 随机返回true或false
if (Math.random() > 0.95) {
return false;
}
return true;
}
}