摘要:主線程名我們啟動(dòng)的一個(gè)程序可以理解為一個(gè)進(jìn)程一個(gè)進(jìn)程中包含一個(gè)主線程線程可以理解為一個(gè)子任務(wù)中可以通過下面代碼來獲取默認(rèn)的主線程名運(yùn)行結(jié)果為這是線程的名字并不是方法通過此線程來執(zhí)行方法而已兩種方式創(chuàng)建線程繼承類實(shí)現(xiàn)接口實(shí)現(xiàn)接口并且多線程運(yùn)行
Java 主線程名
我們啟動(dòng)的一個(gè)程序可以理解為一個(gè)進(jìn)程, 一個(gè)進(jìn)程中包含一個(gè)主線程, 線程可以理解為一個(gè)子任務(wù). Java 中可以通過下面代碼來獲取默認(rèn)的主線程名.
System.out.println(Thread.currentThread().getName());
運(yùn)行結(jié)果為 main, 這是線程的名字并不是 main 方法, 通過此線程來執(zhí)行 main 方法而已.
兩種方式創(chuàng)建線程1.繼承 Thread 類
public class Thread1 extends Thread { @Override public void run() { System.out.println("qwe"); } }
2.實(shí)現(xiàn) Runnable 接口
public class Thread2 implements Runnable { @Override public void run() { System.out.println("asd"); } }
Thread 實(shí)現(xiàn) Runnable 接口. 并且多線程運(yùn)行時(shí), 代碼的執(zhí)行順序與調(diào)用順序是無關(guān)的. 另外如果多次調(diào)用 start方法則會(huì)拋出 java.lang.IllegalThreadStateExceptioncurrentThread 方法
返回當(dāng)前代碼正在被哪個(gè)線程調(diào)用.
public class Thread1 extends Thread { public Thread1() { System.out.println("構(gòu)造方法的打?。? + Thread.currentThread().getName()); } @Override public void run() { System.out.println("run 方法的打?。? + Thread.currentThread().getName()); } }
Thread1 thread1 = new Thread1(); thread1.start();isAlive 方法
判斷當(dāng)前線程是否處于活動(dòng)狀態(tài).
public class Thread1 extends Thread { @Override public void run() { System.out.println("run 方法的打印 Thread.currentThread().isAlive() == " + Thread.currentThread().isAlive()); System.out.println("run 方法的打印 this.isAlive() == " + this.isAlive()); System.out.println("run 方法的打印 Thread.currentThread().isAlive() == this.isAlive() == " + (Thread.currentThread() == this ? "true" : "false")); } }
Thread1 thread1 = new Thread1(); System.out.println("begin == " + thread1.isAlive()); thread1.start(); Thread.sleep(1000); System.out.println("end == " + thread1.isAlive());
執(zhí)行結(jié)果如下
begin == false run 方法的打印 Thread.currentThread().isAlive() == true run 方法的打印 this.isAlive() == true run 方法的打印 Thread.currentThread() == this == true end == false
thread1 在 1秒之后執(zhí)行完成. 所以輸出結(jié)果為 false. 并且 Thread.currentThread() 和 this 是同一個(gè)對(duì)象, 可以理解成執(zhí)行當(dāng)前 run 方法的線程對(duì)象就是我們自己(this).
如果將線程對(duì)象以構(gòu)造參數(shù)的方式傳遞給 Thread 對(duì)象進(jìn)行 start() 啟動(dòng)時(shí), 運(yùn)行結(jié)果和前面實(shí)例是有差異的. 造成這樣的差異的原因還是來自于 Thread.currentThread() 和 this 的差異.
System.out.println("begin == " + thread1.isAlive()); //thread1.start(); // 如果將線程對(duì)象以構(gòu)造參數(shù)的方式傳遞給 Thread 對(duì)象進(jìn)行 start() 啟動(dòng) Thread thread = new Thread(thread1); thread.start(); Thread.sleep(1000); System.out.println("end == " + thread1.isAlive());
執(zhí)行結(jié)果
begin == false run 方法的打印 Thread.currentThread().isAlive() == true run 方法的打印 this.isAlive() == false run 方法的打印 Thread.currentThread() == this == false end == false
Thread.currentThread().isAlive() 是 true 的原因是因?yàn)? Thread.currentThread() 返回的是 thread 對(duì)象, 而我們也是通過此對(duì)象來啟動(dòng)線程的, 所以是在活動(dòng)狀態(tài).
this.isAlive() 是 false 就比較好理解了, 因?yàn)槲覀兪峭ㄟ^ thread 對(duì)象啟動(dòng)的線程來執(zhí)行 run 方法的. 所以它是 false. 同時(shí)也說明這兩個(gè)不是同一個(gè)對(duì)象.
sleep 方法在指定的毫秒數(shù)內(nèi)讓當(dāng)前 "正在執(zhí)行的線程" 休眠. "正在執(zhí)行的線程" 只得是 Thread.currentThread() 返回的線程.
Thread.sleep(1000);停止線程
停止一個(gè)線程意味著在線程處理完任務(wù)之前停掉正在做的操作, 也就是放棄當(dāng)前的操作.
在 Java 中有以下 3 種方法可以終止正在運(yùn)行的線程:
使用退出標(biāo)志, 使線程正常退出, 也就是當(dāng) run 方法完成后線程終止.
使用 stop 方法強(qiáng)行終止線程, 但是不推薦使用這個(gè)方法.
使用 interrupt 方法中斷線程.
停不了的線程調(diào)用 interrupt 方法僅僅是當(dāng)前線程打了一個(gè)停止標(biāo)記, 并不是真正的停止線程.
public class Thread1 extends Thread { @Override public void run() { for(int i = 0; i < 500000; i++) { System.out.println(i); } } }
Thread1 thread1 = new Thread1(); thread1.start(); Thread.sleep(2000); thread1.interrupt();
我們兩秒后調(diào)用 interrupt 方法, 根據(jù)打印結(jié)果我們可以看到線程并沒有停止, 而是在執(zhí)行完 500000 此循環(huán)后 run 方法結(jié)束線程停止.
判斷線程是否是停止?fàn)顟B(tài)我們將線程標(biāo)記為停止后, 需要在線程內(nèi)部判斷一下這個(gè)線程是否是停止標(biāo)記, 如果是則停止線程.
兩種判斷方法:
Thread.interrupted(); 也可以使用 this.interrupted();
this.isInterrupted();
下面是兩個(gè)方法的源碼:
public static boolean interrupted() { return currentThread().isInterrupted(true); } public boolean isInterrupted() { return isInterrupted(false); }
interrupted() 方法數(shù)據(jù)靜態(tài)方法, 也就是判斷當(dāng)前線程是否已經(jīng)中斷. isInterrupted() 判斷線程是否已經(jīng)被中斷.
來自官網(wǎng)的 interrupted() 方法重點(diǎn).異常停止線程
線程的 中斷狀態(tài) 由該方法清除. 換句話說, 如果連續(xù)兩次調(diào)用該方法, 則第二次調(diào)用將返回 false (在第一次調(diào)用已清除了其中斷狀態(tài)之后, 且第二次調(diào)用檢驗(yàn)完中斷狀態(tài)前, 當(dāng)前線程再次中斷的情況除外).
public class Thread1 extends Thread { @Override public void run() { try { for (int i = 0; i < 500000; i++) { if (this.isInterrupted()) { System.out.println("線程停止"); throw new InterruptedException(); } System.out.println("i = " + i); } } catch (InterruptedException e) { System.out.println("線程通過 catch 停止"); e.printStackTrace(); } } }
Thread1 thread1 = new Thread1(); thread1.start(); Thread.sleep(1000); thread1.interrupt();
輸出結(jié)果, 這是最后幾行:
i = 195173 i = 195174 i = 195175 線程停止 線程通過 catch 停止 java.lang.InterruptedException at Thread1.run(Thread1.java:9)
當(dāng)然我們也可以將 throw new InterruptedException(); 換成 return. 都是一樣可以結(jié)束線程的.在沉睡中停止
如果線程調(diào)用 Thread.sleep() 方法使線程進(jìn)行休眠, 這時(shí)我們調(diào)用 thread1.interrupt()后會(huì)拋出. InterruptedException 異常.
java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at Thread1.run(Thread1.java:8)暴力停止線程
暴力停止線程可以使用 stop 方法, 但此方法已經(jīng)過時(shí)并不推薦使用, 原因如下.
即刻拋出 ThreadDeath 異常, 在線程的run()方法內(nèi), 任何一點(diǎn)都有可能拋出ThreadDeath Error, 包括在 catch 或 finally 語句中. 也就是說代碼不確定執(zhí)行到哪一步就會(huì)拋出異常.
釋放該線程所持有的所有的鎖. 這可能會(huì)導(dǎo)致數(shù)據(jù)不一致性.
public class Thread1 extends Thread { private String userName = "a"; private String pwd = "aa"; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public void run() { this.userName = "b"; try { Thread.sleep(100000); } catch (InterruptedException e) { e.printStackTrace(); } this.pwd = "bb"; } }
Thread1 thread1 = new Thread1(); thread1.start(); Thread.sleep(1000); thread1.stop(); System.out.println(thread1.getUserName() + " " + thread1.getPwd());
輸出結(jié)果為:
b aa
我們?cè)诖a中然線程休眠 Thread.sleep(100000); 是為了模擬一些其它業(yè)務(wù)邏輯處理所用的時(shí)間, 在線程處理其它業(yè)務(wù)的時(shí)候, 我們調(diào)用 stop 方法來停止線程.
線程是被停止了也執(zhí)行了 System.out.println(thread1.getUserName() + " " + thread1.getPwd()); 來幫我們輸出結(jié)果, 但是 this.pwd = "bb"; 并沒有被執(zhí)行.
所以, 當(dāng)調(diào)用了 stop 方法后, 線程無論執(zhí)行到哪段代碼, 線程就會(huì)立即退出, 并且會(huì)拋出 ThreadDeath 異常, 而且會(huì)釋放所有鎖, 從而導(dǎo)致數(shù)據(jù)不一致的情況.
interrupt 相比 stop 方法更可控, 而且可以保持?jǐn)?shù)據(jù)一致, 當(dāng)你的代碼邏輯執(zhí)行完一次, 下一次執(zhí)行的時(shí)候, 才會(huì)去判斷并退出線程.
如果大家不怎么理解推薦查看 為什么不能使用Thread.stop()方法? 這篇文章. 下面是另一個(gè)比較好的例子.
如果線程當(dāng)前正持有鎖(此線程可以執(zhí)行代碼), stop之后則會(huì)釋放該鎖. 由于此錯(cuò)誤可能出現(xiàn)在很多地方, 那么這就讓編程人員防不勝防, 極易造成對(duì)象狀態(tài)的不一致. 例如, 對(duì)象 obj 中存放著一個(gè)范圍值: 最小值low, 最大值high, 且low不得大于high, 這種關(guān)系由鎖lock保護(hù), 以避免并發(fā)時(shí)產(chǎn)生競(jìng)態(tài)條件而導(dǎo)致該關(guān)系失效.
假設(shè)當(dāng)前l(fā)ow值是5, high值是10, 當(dāng)線程t獲取lock后, 將low值更新為了15, 此時(shí)被stop了, 真是糟糕, 如果沒有捕獲住stop導(dǎo)致的Error, low的值就為15, high還是10, 這導(dǎo)致它們之間的小于關(guān)系得不到保證, 也就是對(duì)象狀態(tài)被破壞了!
如果在給low賦值的時(shí)候catch住stop導(dǎo)致的Error則可能使后面high變量的賦值繼續(xù), 但是誰也不知道Error會(huì)在哪條語句拋出, 如果對(duì)象狀態(tài)之間的關(guān)系更復(fù)雜呢?這種方式幾乎是無法維護(hù)的, 太復(fù)雜了!如果是中斷操作, 它決計(jì)不會(huì)在執(zhí)行l(wèi)ow賦值的時(shí)候拋出錯(cuò)誤, 這樣程序?qū)τ趯?duì)象狀態(tài)一致性就是可控的.
suspend 與 resume 方法用來暫停和恢復(fù)線程.
獨(dú)占public class PrintObject { synchronized public void printString(){ System.out.println("begin"); if(Thread.currentThread().getName().equals("a")){ System.out.println("線程 a 被中斷"); Thread.currentThread().suspend(); } if(Thread.currentThread().getName().equals("b")){ System.out.println("線程 b 運(yùn)行"); } System.out.println("end"); } }
try{ PrintObject pb = new PrintObject(); Thread thread1 = new Thread(pb::printString); thread1.setName("a"); thread1.start(); thread1.sleep(1000); Thread thread2 = new Thread(pb::printString); thread2.setName("b"); thread2.start(); }catch(InterruptedException e){ }
輸出結(jié)果:
begin 線程 a 被中斷
當(dāng)調(diào)用 Thread.currentThread().suspend(); 方法來暫停線程時(shí), 鎖并不會(huì)被釋放, 所以造成了同步對(duì)象的獨(dú)占.
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/73331.html
摘要:學(xué)習(xí)完多線程之后可以通過下面這些問題檢測(cè)自己是否掌握,下面這些問題的答案以及常見多線程知識(shí)點(diǎn)的總結(jié)在這里??蛇x數(shù)據(jù)結(jié)構(gòu)與算法如果你想進(jìn)入大廠的話,我推薦你在學(xué)習(xí)完基礎(chǔ)或者多線程之后,就開始每天抽出一點(diǎn)時(shí)間來學(xué)習(xí)算法和數(shù)據(jù)結(jié)構(gòu)。 我自己總結(jié)的Java學(xué)習(xí)的系統(tǒng)知識(shí)點(diǎn)以及面試問題,已經(jīng)開源,目前已經(jīng) 35k+ Star。會(huì)一直完善下去,歡迎建議和指導(dǎo),同時(shí)也歡迎Star: https://...
摘要:能理解線程模型多線程優(yōu)缺點(diǎn)以及如何避免。多線程的出現(xiàn)主要是為了提高的利用率任務(wù)的執(zhí)行效率。所以要考慮清楚是否真的需要多線程。這一塊的內(nèi)容可以然我們知道寫大牛處理并發(fā)的思路,對(duì)我們自己編寫高質(zhì)量的多線程程序也有很多幫助。 showImg(https://segmentfault.com/img/remote/1460000015980196?w=2048&h=1363); 前言 已經(jīng)記不...
摘要:后端好書閱讀與推薦這一兩年來養(yǎng)成了買書看書的習(xí)慣,陸陸續(xù)續(xù)也買了幾十本書了,但是一直沒有養(yǎng)成一個(gè)天天看書的習(xí)慣。高級(jí)程序設(shè)計(jì)高級(jí)程序設(shè)計(jì)第版豆瓣有人可能會(huì)有疑問,后端為啥要學(xué)呢其實(shí)就是為了更好的使用做鋪墊。 后端好書閱讀與推薦 這一兩年來養(yǎng)成了買書看書的習(xí)慣,陸陸續(xù)續(xù)也買了幾十本書了,但是一直沒有養(yǎng)成一個(gè)天天看書的習(xí)慣。今天突然想要做個(gè)決定:每天至少花1-3小時(shí)用來看書。這里我準(zhǔn)備把這...
摘要:后端好書閱讀與推薦這一兩年來養(yǎng)成了買書看書的習(xí)慣,陸陸續(xù)續(xù)也買了幾十本書了,但是一直沒有養(yǎng)成一個(gè)天天看書的習(xí)慣。高級(jí)程序設(shè)計(jì)高級(jí)程序設(shè)計(jì)第版豆瓣有人可能會(huì)有疑問,后端為啥要學(xué)呢其實(shí)就是為了更好的使用做鋪墊。 后端好書閱讀與推薦 這一兩年來養(yǎng)成了買書看書的習(xí)慣,陸陸續(xù)續(xù)也買了幾十本書了,但是一直沒有養(yǎng)成一個(gè)天天看書的習(xí)慣。今天突然想要做個(gè)決定:每天至少花1-3小時(shí)用來看書。這里我準(zhǔn)備把這...
摘要:開頭正式開啟我入職的里程,現(xiàn)在已是工作了一個(gè)星期了,這個(gè)星期算是我入職的過渡期,算是知道了學(xué)校生活和工作的差距了,總之,盡快習(xí)慣這種生活吧。當(dāng)時(shí)是看的廖雪峰的博客自己也用做爬蟲寫過幾篇博客,不過有些是在前人的基礎(chǔ)上寫的。 showImg(https://segmentfault.com/img/remote/1460000010867984); 開頭 2017.08.21 正式開啟我...
閱讀 3736·2021-11-17 09:33
閱讀 2760·2021-09-22 15:12
閱讀 3362·2021-08-12 13:24
閱讀 2453·2019-08-30 11:14
閱讀 1745·2019-08-29 14:09
閱讀 1337·2019-08-26 14:01
閱讀 3076·2019-08-26 13:49
閱讀 1790·2019-08-26 12:16