原理介紹:使用interrupt了來通知,而不是強制
使用一個執行緒來通知另一個執行緒該停止的機制,只是一種通知,如果該執行緒本身不決定停止,則其不會停止,被停止執行緒的本身,更熟悉停止自己需要做那些處理和清理工作,所以正確停止執行緒,是如何使用interrupt合理通知該執行緒並讓該執行緒配合停止。
/**
* 描述: run方法內沒有sleep或wait方法時,停止執行緒
*/
public class RightWayStopThreadWithoutSleep implements Runnable {
@Override
public void run() {
int num = 0;
while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
if (num % 10000 == 0) {
System.out.println(num + "是10000的倍數");
}
num++;
}
System.out.println("任務執行結束了");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
}
/**
* 描述: 帶有sleep的中斷執行緒的寫法
*/
public class RightWayStopThreadWithSleep {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
try {
while (num <= 300 && !Thread.currentThread().isInterrupted()) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍數");
}
num++;
}
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(500);
thread.interrupt();
}
}
/**
* 描述: 如果在執行過程中,每次迴圈都會呼叫sleep或wait等方法,那麼不需要每次迭代都檢查是否已中斷
*/
public class RightWayStopThreadWithSleepEveryLoop {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
try {
while (num <= 10000) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍數");
}
num++;
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(5000);
thread.interrupt();
}
}
/**
* 描述: 如果while裡面放try/catch,會導致中斷失效
*/
public class CantInterrupt {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
while (num <= 10000 && !Thread.currentThread().isInterrupted()) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍數");
}
num++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(5000);
thread.interrupt();
}
}
/**
* 描述: catch了InterruptedExcetion之後的優先選擇:在方法簽名中丟擲異常 那麼在run()就會強制try/catch
*/
public class RightWayStopThreadInProd implements Runnable {
@Override
public void run() {
while (true && !Thread.currentThread().isInterrupted()) {
System.out.println("go");
try {
throwInMethod();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
//儲存紀錄檔、停止程式
System.out.println("儲存紀錄檔");
e.printStackTrace();
}
}
}
private void throwInMethod() throws InterruptedException {
Thread.sleep(2000);
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
/**
* 描述:在catch子語句中呼叫Thread.currentThread().interrupt()來恢復設定中斷狀態,以便於在後續的執行中,依然能夠檢查到剛才發生了中斷
* 回到剛才RightWayStopThreadInProd補上中斷,讓它跳出
*/
public class RightWayStopThreadInProd2 implements Runnable {
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Interrupted,程式執行結束");
break;
}
reInterrupt();
}
}
private void reInterrupt() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd2());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
優先選擇:傳遞中斷,使用throw丟擲異常到run方法內集中解決
不想或者無法傳遞: 恢復中斷,在catch中,重新interrupt
不應該遮蔽中斷
錯誤停止的方法
/**
* 描述: 錯誤的停止方法:用stop()來停止執行緒,會導致執行緒執行一半突然停止,沒辦法完成一個基本單位的操作(一個連隊),會造成髒資料(有的連隊多領取少領取裝備)。
*/
public class StopThread implements Runnable {
@Override
public void run() {
//模擬指揮軍隊:一共有5個連隊,每個連隊10人,以連隊為單位,發放武器彈藥,叫到號的士兵前去領取
for (int i = 0; i < 5; i++) {
System.out.println("連隊" + i + "開始領取武器");
for (int j = 0; 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) {
Thread thread = new Thread(new StopThread());
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.stop();
}
}
/**
* 描述: 演示用volatile的侷限:part1 看似可行
*/
public class WrongWayVolatile implements Runnable {
private volatile boolean canceled = false;
@Override
public void run() {
int num = 0;
try {
while (num <= 100000 && !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();
Thread.sleep(5000);
r.canceled = true;
}
}
/**
* 描述:演示用volatile的侷限part2 陷入阻塞時,volatile是無法執行緒的 此例中,生產者的生產速度很快,消費者消費速度慢,所以阻塞佇列滿了以後,生產者會阻塞,等待消費者進一步消費
*/
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();
Thread.sleep(1000);
Consumer consumer = new Consumer(storage);
while (consumer.needMoreNums()) {
System.out.println(consumer.storage.take()+"被消費了");
Thread.sleep(100);
}
System.out.println("消費者不需要更多資料了。");
//一旦消費不需要更多資料了,我們應該讓生產者也停下來,但是實際情況
producer.canceled=true;
System.out.println(producer.canceled);
}
}
class Producer implements Runnable {
public volatile boolean canceled = false;
BlockingQueue storage;
public Producer(BlockingQueue storage) {
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
while (num <= 100000 && !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() {
if (Math.random() > 0.95) {
return false;
}
return true;
}
}
/**
* 描述: 用中斷來修復剛才的無盡等待問題
*/
public class WrongWayVolatileFixed {
public static void main(String[] args) throws InterruptedException {
WrongWayVolatileFixed body = new WrongWayVolatileFixed();
ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
Producer producer = body.new Producer(storage);
Thread producerThread = new Thread(producer);
producerThread.start();
Thread.sleep(1000);
Consumer consumer = body.new Consumer(storage);
while (consumer.needMoreNums()) {
System.out.println(consumer.storage.take() + "被消費了");
Thread.sleep(100);
}
System.out.println("消費者不需要更多資料了。");
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 <= 100000 && !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() {
if (Math.random() > 0.95) {
return false;
}
return true;
}
}
}
判斷是否已經被中斷的方法
/**
* 描述: 注意Thread.interrupted()方法的目標物件是「當前執行緒」,而不管本方法來自於哪個物件
*/
public class RightWayInterrupted {
public static void main(String[] args) throws InterruptedException {
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
for (; ; ) {
}
}
});
// 啟動執行緒
threadOne.start();
//設定中斷標誌
threadOne.interrupt();
//獲取中斷標誌
System.out.println("isInterrupted: " + threadOne.isInterrupted());
//獲取中斷標誌並重置
System.out.println("isInterrupted: " + threadOne.interrupted());
//獲取中斷標誌並重直
System.out.println("isInterrupted: " + Thread.interrupted());
//獲取中斷標誌
System.out.println("isInterrupted: " + threadOne.isInterrupted());
threadOne.join();
System.out.println("Main thread is over.");
}
}
最終結果為
isInterrupted: true
isInterrupted: false
isInterrupted: false
isInterrupted: true
1.如何停止執行緒:
1.原理:用interrupt來請求,好處(保證資料安全,把中斷許可權交給被中斷執行緒)
2.想停止執行緒,要請求方,被停止方,子方法被呼叫方法相互配合
3.最後再說錯誤的方法:stop/suspend被廢棄,volatile的boolean無法處理長時間阻塞的狀態
2.如何處理不可中斷的阻塞
1.不存在通用的解決方案,針對特定情況,需要去找可以相應中斷的方法