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