Java線程的停止實現(xiàn)原理詳解
線程停止的原理
使用interrupt來通知,而不是強(qiáng)制
java提供了interrrupt讓一個線程來通知另一個線程停止
如果想中斷一個線程,但是那個線程不想去中斷,那就無能為力,我們沒有強(qiáng)制去中斷線程的手段,因為線程停止前需要做一定的收尾工作
所以正確停止線程,是如何用interrupt來通知那個線程,以及被停止的線程如何進(jìn)行配合
如何正確停止線程
在普通情況下停止線程
代碼展示
- 調(diào)用interrupt沒有作用
- 下面這段代碼,執(zhí)行interrupt之后,線程并沒有被中斷
- 因為被執(zhí)行的線程并沒有相應(yīng)中斷的方式
public class stopThreadWithoutSleep implements Runnable{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new stopThreadWithoutSleep());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
@Override
public void run() {
int num = 0;
while(num <= Integer.MAX_VALUE / 2) {
if (num % 10000 == 0) {
System.out.println(num + "是10000的倍數(shù)");
}
num++;
}
System.out.println("結(jié)束");
}
}
/*
由于太長,只展示結(jié)尾部分的結(jié)果
1073710000是10000的倍數(shù)
1073720000是10000的倍數(shù)
1073730000是10000的倍數(shù)
1073740000是10000的倍數(shù)
結(jié)束
* */
- 被執(zhí)行線程加上相應(yīng)中斷的操作之后
- 結(jié)果可知,被執(zhí)行線程相應(yīng)一秒之后就結(jié)束了
public class stopThreadWithoutSleep implements Runnable{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new stopThreadWithoutSleep());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
@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的倍數(shù)");
}
num++;
}
System.out.println("結(jié)束");
}
}
/*
由于太長,只展示結(jié)尾部分的結(jié)果
587830000是10000的倍數(shù)
587840000是10000的倍數(shù)
587850000是10000的倍數(shù)
587860000是10000的倍數(shù)
結(jié)束
* */
在阻塞情況下停止線程
代碼展示
- 中斷之后,拋出異常
- 線程在sleep的過程中,會catch到InterruptedException這個異常,從而相應(yīng)中斷
public class stopThreadWithSleep {
public static void main(String[] args) {
Runnable runnable = () -> {
int num = 0;
while (num <= 300 && !Thread.currentThread().isInterrupted()) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍數(shù)");
}
num++;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
}
}
/*
* 0是100的倍數(shù)
100是100的倍數(shù)
200是100的倍數(shù)
300是100的倍數(shù)
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.jx.JavaTest.stopThread.stopThreadWithSleep.lambda$main$0(stopThreadWithSleep.java:15)
at java.lang.Thread.run(Thread.java:748)
* */
線程在每次迭代后都阻塞
- 代碼展示 即使不在while判斷是否中斷,sleep也能中斷異常
public class stopThreadWithSleepEveryLoop {
public static void main(String[] args) {
Runnable runnable = () -> {
int num = 0;
try {
while (num <= 10000) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍數(shù)");
}
num++;
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
try {
Thread.sleep(5000);
} catch (
InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
}
}
/*
* 0是100的倍數(shù)
100是100的倍數(shù)
200是100的倍數(shù)
300是100的倍數(shù)
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.jx.JavaTest.stopThread.stopThreadWithSleepEveryLoop.lambda$main$0(stopThreadWithSleepEveryLoop.java:15)
at java.lang.Thread.run(Thread.java:748)
Process finished with exit code 0
*
* */
當(dāng)catch寫到while內(nèi),則不能正常中斷
public class CantInterrupt {
public static void main(String[] args) {
Runnable runnable = () -> {
int num = 0;
while (num <= 10000) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍數(shù)");
}
num ++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
try {
Thread.sleep(5000);
} catch (
InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
}
}
/*
* 0是100的倍數(shù)
100是100的倍數(shù)
200是100的倍數(shù)
300是100的倍數(shù)
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.jx.JavaTest.stopThread.CantInterrupt.lambda$main$0(CantInterrupt.java:14)
at java.lang.Thread.run(Thread.java:748)
400是100的倍數(shù)
500是100的倍數(shù)
600是100的倍數(shù)
700是100的倍數(shù)
800是100的倍數(shù)
900是100的倍數(shù)
Process finished with exit code -1
* */
- 即使在while的判斷條件中,加上檢測中斷的機(jī)制,也不能正常中斷
- 因為java的sleep函數(shù),一旦相應(yīng)中斷,就會將中斷的標(biāo)志位刪除
public class CantInterrupt {
public static void main(String[] args) {
Runnable runnable = () -> {
int num = 0;
while (num <= 10000 && !Thread.currentThread().isInterrupted()) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍數(shù)");
}
num++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
try {
Thread.sleep(5000);
} catch (
InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
}
}
/*
0是100的倍數(shù)
100是100的倍數(shù)
200是100的倍數(shù)
300是100的倍數(shù)
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.jx.JavaTest.stopThread.CantInterrupt.lambda$main$0(CantInterrupt.java:14)
at java.lang.Thread.run(Thread.java:748)
400是100的倍數(shù)
500是100的倍數(shù)
Process finished with exit code -1
* */
停止線程的最佳實踐
- 在方法簽名中拋出異常,在run方法中強(qiáng)制進(jìn)行try catch
public class StopThreadInProd implements Runnable{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new StopThreadInProd());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
@Override
public void run() {
while (true) {
System.out.println("start");
try {
throwInMethod();
} catch (InterruptedException e) {
System.out.println("保存日志/關(guān)閉程序");
e.printStackTrace();
}
}
}
private void throwInMethod() throws InterruptedException {
Thread.sleep(2000);
}
}
/*
* start
保存日志/關(guān)閉程序
start
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.jx.JavaTest.stopThread.StopThreadInProd.throwInMethod(StopThreadInProd.java:26)
at com.jx.JavaTest.stopThread.StopThreadInProd.run(StopThreadInProd.java:17)
at java.lang.Thread.run(Thread.java:748)
start
start
Process finished with exit code -1
*
* */
- 在catch語句中調(diào)用Thread.currentThread().interrupt恢復(fù)中斷狀態(tài)
- 結(jié)果:拋出異常,程序結(jié)束
public class StopThreadInProd2 implements Runnable{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new StopThreadInProd2());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Interrupt");
break;
}
reInterrupt();
}
}
private void reInterrupt() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}
/*
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.jx.JavaTest.stopThread.StopThreadInProd2.reInterrupt(StopThreadInProd2.java:25)
at com.jx.JavaTest.stopThread.StopThreadInProd2.run(StopThreadInProd2.java:19)
at java.lang.Thread.run(Thread.java:748)
Interrupt
*
* */
- 依照上面的代碼,如果方法沒有沒有重新拋出異常
- 結(jié)果:程序拋出異常,但是程序沒有停止運(yùn)行
public class StopThreadInProd2 implements Runnable{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new StopThreadInProd2());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Interrupt");
break;
}
reInterrupt();
}
}
private void reInterrupt() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}
/*
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.jx.JavaTest.stopThread.StopThreadInProd2.reInterrupt(StopThreadInProd2.java:25)
at com.jx.JavaTest.stopThread.StopThreadInProd2.run(StopThreadInProd2.java:19)
at java.lang.Thread.run(Thread.java:748)
*
* */
錯誤停止的方法
被棄用的stop,suspend和resume方法
- 使用stop停止線程,會導(dǎo)致線程運(yùn)行一半突然停止,沒辦法完成最基本的操作,會造成臟數(shù)據(jù)
- 下面這段代碼的結(jié)果會造成一個連隊只有部分人領(lǐng)取到了裝備
- stop是不安全的,會直接停止監(jiān)視器
- suspend和resume不會破壞對象,但是會讓線程掛起,不釋放鎖,容易造成死鎖
public class StopThread implements Runnable{
@Override
public void run() {
// 模擬指揮軍隊,一共五個連隊,每個連隊一百人
// 以連隊為單位發(fā)放武器
for (int i = 0; i < 5; i++) {
System.out.println("連隊" + i + "領(lǐng)取武器");
for (int j = 0; j < 10; j++) {
System.out.println(j);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("連隊" + i + "領(lǐng)取完畢");
}
}
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();
}
}
/*
* 連隊0領(lǐng)取武器
0
1
2
3
4
5
6
7
8
9
連隊0領(lǐng)取完畢
連隊1領(lǐng)取武器
0
1
2
3
4
5
Process finished with exit code 0
* */
用volatile設(shè)置boolean標(biāo)記位
- 下面這段代碼,通過改變標(biāo)志位的值會成功終止線程
public class Volatile implements Runnable {
private volatile boolean canceled = false;
@Override
public void run() {
int num = 0;
try {
while (num <= 10000 && !canceled) {
if (num % 100 == 0) {
System.out.println(num + " 是100的倍數(shù)");
}
num++;
Thread.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Volatile v = new Volatile();
Thread thread = new Thread(v);
thread.start();
Thread.sleep(1000);
v.canceled = true;
}
}
/*
*0 是100的倍數(shù)
100 是100的倍數(shù)
200 是100的倍數(shù)
300 是100的倍數(shù)
400 是100的倍數(shù)
500 是100的倍數(shù)
600 是100的倍數(shù)
Process finished with exit code 0
*
* */
- 當(dāng)陷入阻塞的時候,是無法停止線程的
- 下面這段代碼的運(yùn)行結(jié)果,并沒有打印生產(chǎn)者停止運(yùn)行,說明根本沒有執(zhí)行生產(chǎn)者的finally那部分代碼
- 同時程序也沒停止
- 原因見生產(chǎn)者代碼 while循環(huán)中的注釋
// 模擬生產(chǎn)者和消費(fèi)者
public class cantStop {
public static void main(String[] args) throws InterruptedException {
// 阻塞隊列
// 滿了之后,放不進(jìn)去
// 空的時候取數(shù)據(jù),也會堵塞
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.needMore()) {
System.out.println(consumer.storage.take() + "被消費(fèi)");
Thread.sleep(100);
}
System.out.println("消費(fèi)者不需要更多數(shù)據(jù)");
// 消費(fèi)者不需要數(shù)據(jù),讓生產(chǎn)者停下來
producer.canceled = true;
}
}
// 生產(chǎn)者
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 <= 10000 && !canceled) {
if (num % 100 == 0) {
// 當(dāng)堵塞隊列滿了之后,會堵塞在這里,而這段代碼沒有判斷機(jī)制
storage.put(num);
System.out.println("num" + "生產(chǎn)");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生產(chǎn)者停止運(yùn)行");
}
}
}
// 消費(fèi)者
class Consumer {
BlockingQueue storage;
public Consumer(BlockingQueue storage) {
this.storage = storage;
}
public boolean needMore() {
if (Math.random() > 0.9) {
return false;
}
return true;
}
}
/*
* num生產(chǎn)
num生產(chǎn)
num生產(chǎn)
num生產(chǎn)
num生產(chǎn)
num生產(chǎn)
num生產(chǎn)
num生產(chǎn)
num生產(chǎn)
num生產(chǎn)
0被消費(fèi)
num生產(chǎn)
消費(fèi)者不需要更多數(shù)據(jù)
*
* */
- 將上面代碼用interrupt進(jìn)行中斷
- 程序成功停止
public class finxed {
public static void main(String[] args) throws InterruptedException {
finxed finxed = new finxed();
// 阻塞隊列
// 滿了之后,放不進(jìn)去
// 空的時候取數(shù)據(jù),也會堵塞
ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
Producer producer = finxed.new Producer(storage);
Thread producerThread = new Thread(producer);
producerThread.start();
Thread.sleep(1000);
Consumer consumer = finxed.new Consumer(storage);
while (consumer.needMore()) {
System.out.println(consumer.storage.take() + "被消費(fèi)");
Thread.sleep(100);
}
System.out.println("消費(fèi)者不需要更多數(shù)據(jù)");
// 消費(fèi)者不需要數(shù)據(jù),讓生產(chǎn)者停下來
producerThread.interrupt();
}
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 <= 10000 && !Thread.currentThread().isInterrupted()) {
if (num % 100 == 0) {
storage.put(num);
System.out.println("num" + "生產(chǎn)");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生產(chǎn)者停止運(yùn)行");
}
}
}
class Consumer {
BlockingQueue storage;
public Consumer(BlockingQueue storage) {
this.storage = storage;
}
public boolean needMore() {
if (Math.random() > 0.9) {
return false;
}
return true;
}
}
}
/*
* 2100被消費(fèi)
num生產(chǎn)
2200被消費(fèi)
num生產(chǎn)
2300被消費(fèi)
num生產(chǎn)
消費(fèi)者不需要更多數(shù)據(jù)
生產(chǎn)者停止運(yùn)行
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
at java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:353)
at com.jx.JavaTest.stopThread.volatiledmo.finxed$Producer.run(finxed.java:51)
at java.lang.Thread.run(Thread.java:748)
Process finished with exit code 0
*
* */interrupt源碼查看
- 這段代碼做的都是一些判斷,真正執(zhí)行中斷的代碼時interrupt0
- interrupt0是native代碼
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
private native void interrupt0();
interrupt相關(guān)函數(shù)練習(xí)
- isInterrupted獲取中斷標(biāo)志,獲取的是前面的線程
- interrupted獲取中斷標(biāo)志并重置,只關(guān)心執(zhí)行的線程,所以下面代碼執(zhí)行的是main線程
public class InterruptedTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
}
}
});
thread.start();
thread.interrupt();
// 獲取中斷標(biāo)志
System.out.println(thread.isInterrupted()); // true
// 獲取中斷標(biāo)志并重置
System.out.println(thread.interrupted()); //false
System.out.println(Thread.interrupted()); // false
System.out.println(thread.isInterrupted()); //true
thread.join();
System.out.println("over");
}
}
到此這篇關(guān)于Java線程的停止實現(xiàn)原理詳解的文章就介紹到這了,更多相關(guān)Java線程的停止內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中數(shù)組與集合的相互轉(zhuǎn)換實現(xiàn)解析
這篇文章主要介紹了Java中數(shù)組與集合的相互轉(zhuǎn)換實現(xiàn)解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-08-08
Struts2中validate數(shù)據(jù)校驗的兩種方法詳解附Struts2常用校驗器
這篇文章主要介紹了Struts2中validate數(shù)據(jù)校驗的兩種方法及Struts2常用校驗器,本文介紹的非常詳細(xì),具有參考借鑒價值,感興趣的朋友一起看看吧2016-09-09
Java創(chuàng)建數(shù)組、賦值的四種方式詳解(聲明+創(chuàng)建+初始化?)
數(shù)組是一種數(shù)據(jù)結(jié)構(gòu),用來存儲同一類型值的集合一旦創(chuàng)建了數(shù)組,就不能再改變它的長度,下面這篇文章主要給大家介紹了關(guān)于Java創(chuàng)建數(shù)組、賦值的四種方式(聲明+創(chuàng)建+初始化?)的相關(guān)資料,需要的朋友可以參考下2024-04-04
Java數(shù)組操作經(jīng)典例題大總結(jié)
數(shù)組是在內(nèi)存中存儲相同數(shù)據(jù)類型的連續(xù)的空間,聲明一個數(shù)組就是在內(nèi)存空間中劃出一串連續(xù)的空間,下面這篇文章主要給大家介紹了關(guān)于Java數(shù)組操作經(jīng)典例題的相關(guān)資料,需要的朋友可以參考下2022-03-03
使用JWT作為Spring?Security?OAuth2的token存儲問題
這篇文章主要介紹了使用JWT作為Spring?Security?OAuth2的token存儲,大家經(jīng)常使用的方法有兩種一種是使用JWT作為Token傳遞,一種是使用Redis存儲Token,資源服務(wù)器本地訪問Redis校驗Token,需要的朋友可以參考下2021-12-12
Springboot根據(jù)配置文件動態(tài)注入接口實現(xiàn)類詳解
這篇文章主要介紹了Springboot根據(jù)配置文件動態(tài)注入接口實現(xiàn)類詳解,具有很好的參考價值,希望對大家有所幫助,需要的朋友可以參考下,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10
SpringBoot整合Liquibase實現(xiàn)對數(shù)據(jù)庫管理和遷移
Liquibase是一個用于用于跟蹤、管理和應(yīng)用數(shù)據(jù)庫變化的開源工具,通過日志文件(changelog)的形式記錄數(shù)據(jù)庫的變更(changeset),然后執(zhí)行日志文件中的修改,將數(shù)據(jù)庫更新或回滾(rollback)到一致的狀態(tài),本文主要介紹SpringBoot與Liquibase的集成,需要的朋友可以參考下2024-11-11

