1. 被棄用的 stop弱卡、suspend 和 resume 方法
用 stop() 來(lái)停止線(xiàn)程画株,會(huì)導(dǎo)致線(xiàn)程運(yùn)行一半突然停止谦铃,沒(méi)辦法完成一個(gè)基本單位的操作,會(huì)造成臟數(shù)據(jù)赞厕;
模擬連隊(duì)發(fā)裝備代碼示例:
public class StopThread implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("連隊(duì)" + i + " 開(kāi)始領(lǐng)妊藓:");
for (int j = 1; j <= 10; j++) {
System.out.println("士兵" + j + " 開(kāi)始領(lǐng)取");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("====連隊(duì)" + i + "領(lǐng)取完畢====");
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new StopThread());
thread.start();
// 等待666毫秒,停止線(xiàn)程
Thread.sleep(666);
thread.stop();
}
}
運(yùn)行結(jié)果:
連隊(duì)1 開(kāi)始領(lǐng)让笊!:
士兵1 開(kāi)始領(lǐng)取
士兵2 開(kāi)始領(lǐng)取
士兵3 開(kāi)始領(lǐng)取
士兵4 開(kāi)始領(lǐng)取
士兵5 開(kāi)始領(lǐng)取
士兵6 開(kāi)始領(lǐng)取
士兵7 開(kāi)始領(lǐng)取
士兵8 開(kāi)始領(lǐng)取
士兵9 開(kāi)始領(lǐng)取
士兵10 開(kāi)始領(lǐng)取
====連隊(duì)1領(lǐng)取完畢====
連隊(duì)2 開(kāi)始領(lǐng)冉┏邸:
士兵1 開(kāi)始領(lǐng)取
士兵2 開(kāi)始領(lǐng)取
士兵3 開(kāi)始領(lǐng)取
Process finished with exit code 0
會(huì)發(fā)現(xiàn) 連隊(duì)2 才3個(gè)人領(lǐng)完,其他人都還沒(méi)有領(lǐng)到唁毒,線(xiàn)程突然就結(jié)束了,這樣就會(huì)造成數(shù)據(jù)的錯(cuò)亂星爪!
并且這種數(shù)據(jù)的錯(cuò)亂后期難以排查浆西!
Oracle官方文檔對(duì)于為什么Thread.stop不推薦使用的解釋
https://docs.oracle.com/javase/7/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
suspend和resume被拋棄原因:
suspend和stop不一樣,suspend不會(huì)破壞對(duì)象顽腾,但是它會(huì)讓一個(gè)線(xiàn)程掛起近零,在恢復(fù)之前,鎖不會(huì)釋放抄肖,也就是它是帶著鎖去進(jìn)行休息的久信,這樣的話(huà)很容易造成死鎖。
resume
2. 用volatile設(shè)置boolean標(biāo)記位
2.1:看似可行的代碼
public class WrongWayVolatile implements Runnable {
/**
* 設(shè)置boolean類(lèi)型的標(biāo)記位
*/
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值以達(dá)到停止程序的目的
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的倍數(shù)");
}
num++;
Thread.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
程序在運(yùn)行5秒鐘后停止了漓摩,達(dá)到了預(yù)期的效果裙士。
2.2:當(dāng)陷入阻塞時(shí),無(wú)法停止
下面代碼示例中管毙,生產(chǎn)者的生產(chǎn)速度很快腿椎,消費(fèi)者消費(fèi)速度慢,
所以阻塞隊(duì)列滿(mǎn)了以后夭咬,生產(chǎn)者會(huì)阻塞停止生產(chǎn)啃炸,等待消費(fèi)者進(jìn)一步消費(fèi)
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秒讓生產(chǎn)者將隊(duì)列填滿(mǎn)
Thread.sleep(1000);
Consumer consumer = new Consumer(storage);
while (consumer.needMoreNums()) {
System.out.println(consumer.storage.take() + "被消費(fèi)了!");
// 消費(fèi)是需要時(shí)間的卓舵,設(shè)置個(gè)100毫秒
Thread.sleep(100);
}
System.out.println("消費(fèi)者不需要更多數(shù)據(jù)了南用。");
// 一旦消費(fèi)者不需要更多數(shù)據(jù)了,則應(yīng)該讓生產(chǎn)者停下來(lái)掏湾,
// 將標(biāo)記位設(shè)置為true裹虫,看是否能將線(xiàn)程停止?
producer.canceled = true;
System.out.println(producer.canceled);
}
}
/**
* 生產(chǎn)者
*/
class Producer implements Runnable {
/**
* 設(shè)置boolean類(lèi)型的標(biāo)記位
*/
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的倍數(shù)融击,被放到了倉(cāng)庫(kù)中恒界。");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生產(chǎn)者結(jié)束運(yùn)行");
}
}
}
/**
* 消費(fèi)者
*/
class Consumer {
BlockingQueue storage;
public Consumer(BlockingQueue storage) {
this.storage = storage;
}
public boolean needMoreNums() {
// 隨機(jī)返回true或false
if (Math.random() > 0.95) {
return false;
}
return true;
}
}
最后的運(yùn)行結(jié)果并沒(méi)有打印出 “生產(chǎn)者結(jié)束運(yùn)行”,
而且程序也沒(méi)有停止Q庾臁Jā涩拙!
為什么?
因?yàn)樯a(chǎn)者暫停生產(chǎn)時(shí)耸采,是阻塞在 storage.put(num);
而且也沒(méi)有人去喚醒兴泥,所以 while() 條件也無(wú)法執(zhí)行,也不知道 canceled 的值已經(jīng)被改變虾宇!
只會(huì)一直等在 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秒讓生產(chǎn)者將隊(duì)列填滿(mǎn)
Thread.sleep(1000);
Consumer consumer = new Consumer(storage);
while (consumer.needMoreNums()) {
System.out.println(consumer.storage.take() + "被消費(fèi)了!");
// 消費(fèi)是需要時(shí)間的嘱朽,設(shè)置個(gè)100毫秒
Thread.sleep(100);
}
System.out.println("消費(fèi)者不需要更多數(shù)據(jù)了旭贬。");
// 一旦消費(fèi)者不需要更多數(shù)據(jù)了,則應(yīng)該讓生產(chǎn)者停下來(lái)搪泳,
// 使用interrupt通知停止線(xiàn)程
producerThread.interrupt();
}
}
/**
* 生產(chǎn)者
*/
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的倍數(shù)稀轨,被放到了倉(cāng)庫(kù)中。");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生產(chǎn)者結(jié)束運(yùn)行");
}
}
}
/**
* 消費(fèi)者
*/
class Consumer {
BlockingQueue storage;
public Consumer(BlockingQueue storage) {
this.storage = storage;
}
public boolean needMoreNums() {
// 隨機(jī)返回true或false
if (Math.random() > 0.95) {
return false;
}
return true;
}
}