我們知道像stop、suspend這幾種中斷或者阻塞線程的方法在較高java版本中已經(jīng)被標(biāo)記上了@Deprecated過(guò)期標(biāo)簽,那么為什么她們?cè)?jīng)登上了java的歷史舞臺(tái)而又漸漸的推出了舞臺(tái)呢,到底是人性的扭曲還是道德的淪喪呢,亦或是她們不思進(jìn)取被取而代之呢,如果是被取而代之,那么取而代之的又是何方人也,本文我們將一探究竟。
一、stop的落幕首先stop方法的作用是什么呢,用java源碼中的一句注釋來(lái)了解一下:Forces the thread to stop executing.,即強(qiáng)制線程停止執(zhí)行,‘Forces’似乎已經(jīng)透漏出了stop方法的蠻狠無(wú)理。那么我們?cè)倏纯磈ava開(kāi)發(fā)者是怎們解釋stop被淘汰了的。
我們從中可以看出以下幾點(diǎn):
stop這種方法本質(zhì)上是不安全的
使用Thread.stop停止線程會(huì)導(dǎo)致它解鎖所有已鎖定的監(jiān)視器,即直接釋放當(dāng)前線程已經(jīng)獲取到的所有鎖,使得當(dāng)前線程直接進(jìn)入阻塞狀態(tài)
我們舉例來(lái)看一下上邊提到的兩點(diǎn):
public static void main(String[] args) throws InterruptedException {
Object o1=new Object();
Object o2=new Object();
Thread t1=new Thread(()-》{
synchronized (o1)
{
synchronized (o2)
{
try {
System.out.println(“t1獲取到鎖”);
Thread.sleep(5000);
System.out.println(“t1結(jié)束”);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t1.start();
Thread.sleep(1000);
Thread t2=new Thread(()-》{
synchronized (o1)
{
synchronized (o2)
{
try {
System.out.println(“t2獲取到鎖”);
Thread.sleep(5000);
System.out.println(“t2結(jié)束”);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t2.start();
t1.stop();
}
運(yùn)行結(jié)果:
可以看到,當(dāng)線程t1在獲取到o1和o2兩個(gè)鎖開(kāi)始執(zhí)行,在還沒(méi)有執(zhí)行結(jié)束的時(shí)候,主線程調(diào)用了t1的stop方法中斷了t1的執(zhí)行,釋放了t1線程獲取到的所有鎖,中斷后t2獲取到了o1和o2鎖,開(kāi)始執(zhí)行直到結(jié)束,而t1卻夭折在了sleep的時(shí)候,sleep后的代碼沒(méi)有執(zhí)行。
因此使用stop我們?cè)诓恢谰€程到底運(yùn)行到了什么地方,暴力的中斷了線程,如果sleep后的代碼是資源釋放、重要業(yè)務(wù)邏輯等比較重要的代碼的話,亦或是其他線程依賴t1線程的運(yùn)行結(jié)果,那直接中斷將可能造成很嚴(yán)重的后果。
那么不建議使用stop中斷線程我們應(yīng)該怎么去優(yōu)雅的結(jié)束一個(gè)線程呢,我們可以存java開(kāi)發(fā)者的注釋中窺探到一種解決方案:
可以看到j(luò)ava開(kāi)發(fā)者推薦我們使用以下兩種方法來(lái)優(yōu)雅的停止線程:
1.定義一個(gè)變量,由目標(biāo)線程去不斷的檢查變量的狀態(tài),當(dāng)變量達(dá)到某個(gè)狀態(tài)時(shí)停止線程。
代碼舉例如下:
volatile static boolean flag=false;
public static void main(String[] args) throws InterruptedException {
Object o1=new Object();
Thread t1=new Thread(()-》{
synchronized (o1)
{
try {
System.out.println(“t1獲取到鎖”);
while (!flag)
Thread.sleep(5000);//執(zhí)行業(yè)務(wù)邏輯
System.out.println(“t1結(jié)束”);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
Thread.sleep(1000);
Thread t2=new Thread(()-》{
synchronized (o1)
{
try {
System.out.println(“t2獲取到鎖”);
Thread.sleep(5000);//執(zhí)行業(yè)務(wù)邏輯
System.out.println(“t2結(jié)束”);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t2.start();
flag=true;
}
運(yùn)行結(jié)果:
2.使用interrupt方法中斷線程。
代碼舉例如下:
public static void main(String[] args) throws InterruptedException {
Object o1=new Object();
Thread t1=new Thread(()-》{
synchronized (o1)
{
System.out.println(“t1獲取到鎖”);
while (!Thread.currentThread().isInterrupted()) {
for (int i = 0; i 《 100; i++) {
if(i==50)
System.out.println();
System.out.print(i+“ ”);
}
System.out.println();
}
System.out.println(“t1結(jié)束”);
}
});
t1.start();
Thread t2=new Thread(()-》{
synchronized (o1)
{
try {
System.out.println(“t2獲取到鎖”);
Thread.sleep(5000);//執(zhí)行業(yè)務(wù)邏輯
System.out.println(“t2結(jié)束”);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t2.start();
t1.interrupt();
}
運(yùn)行結(jié)果:
我們用while (!Thread.currentThread().isInterrupted())來(lái)不斷判斷當(dāng)前線程是否被中斷,中斷的話則讓線程自然消亡并釋放鎖??梢钥吹秸{(diào)用interrupt方法后并不會(huì)像stop那樣暴力的中斷線程,會(huì)等到當(dāng)前運(yùn)行的邏輯結(jié)束后再檢查是否中斷,非常的優(yōu)雅。另外,關(guān)注Java知音公眾號(hào),回復(fù)“后端面試”,送你一份面試題寶典!
注:運(yùn)行舉例代碼可能不會(huì)打印出數(shù)字,這是因?yàn)閠1線程運(yùn)行到while(!Thread.currentThread().isInterrupted())時(shí),主線程已經(jīng)調(diào)了interrupt方法,因此多次運(yùn)行可能會(huì)打印出數(shù)字。
二、suspend的落幕suspend方法的作用是掛起某個(gè)線程直到調(diào)用resume方法來(lái)恢復(fù)該線程,但是調(diào)用了suspend方法后并不會(huì)釋放被掛起線程獲取到的鎖,正因如此就給suspend和resume這哥倆貼上了容易引發(fā)死鎖的標(biāo)簽,當(dāng)然這也正是導(dǎo)致suspend和resume退出歷史舞臺(tái)的罪魁禍?zhǔn)住M瑯游覀兛纯磈ava開(kāi)發(fā)者為suspend的淘汰給出的理由:
從中我們可以得出以下結(jié)論:
suspend具有天然的死鎖傾向
當(dāng)某個(gè)線程被suspend后,該線程持有的鎖不會(huì)被釋放,其他線程也就不能訪問(wèn)這些資源
suspend某個(gè)線程后,如果在resume的過(guò)程中出現(xiàn)異常導(dǎo)致resume方法執(zhí)行失敗,則lock無(wú)法釋放,導(dǎo)致死鎖
接下來(lái)模擬一下由suspend引起的死鎖場(chǎng)景,Talk is cheap,show my code:
public static void main(String[] args) throws InterruptedException {
Object o1=new Object();
Object o2=new Object();
Thread t1=new Thread(()-》{
synchronized (o1)
{
System.out.println(“t1獲取到o1鎖開(kāi)始執(zhí)行”);
try {
Thread.sleep(5000);//模擬執(zhí)行業(yè)務(wù)邏輯
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(“t1執(zhí)行結(jié)束”);
}
});
t1.start();
Thread t2=new Thread(()-》{
synchronized (o2)
{
System.out.println(“t2獲取到o2開(kāi)始執(zhí)行”);
try {
Thread.sleep(2000);//執(zhí)行耗時(shí)業(yè)務(wù)
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1)
{
System.out.println(“t2獲取到o1鎖開(kāi)始繼續(xù)執(zhí)行”);
}
System.out.println(“t2執(zhí)行結(jié)束”);
}
});
t2.start();
Thread.sleep(1000);
t1.suspend();
//假設(shè)拋出了一個(gè)未知異常
int i=1/0;
t1.resume();
}
運(yùn)行結(jié)果:
可以看到,整個(gè)程序卡的死死的,在調(diào)用resume恢復(fù)t1線程之前拋出了一個(gè)未知異常,導(dǎo)致t1一直掛起進(jìn)而無(wú)法釋放o1鎖,而t2需要獲取到o1鎖后才能繼續(xù)執(zhí)行,但苦苦等待,奈何o1被t1拿捏的死死的,從此整個(gè)程序就陷入了無(wú)盡的等待中----死鎖。
作者丨浪舟子
blog.csdn.net/qq_40400960/article/details/112651249
編輯:jq
-
中斷
+關(guān)注
關(guān)注
5文章
899瀏覽量
41552
原文標(biāo)題:為什么強(qiáng)烈不推薦使用stop、suspend方法來(lái)中斷線程?
文章出處:【微信號(hào):AndroidPush,微信公眾號(hào):Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論