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