執行緒的基本方法
- 2020 年 3 月 10 日
- 筆記
1. 進程與執行緒
進程:資源分配的基本單位
執行緒:資源調度的基本單位
1.1 有了進程為什麼還需要執行緒呢?
為了進程能進行並發操作
1.2 執行緒的生命周期
2. 創建進程
創建進程有兩種方法(一般推薦第二種,因為單繼承問題)
先來看看執行緒的構造函數
2.1 繼承Thread類,重寫run()
public class Threadtest extends Thread { //設置名字 public Threadtest(String name) { super(name); } //重寫方法 public void run(){ for(int i = 0;i < 100;i++){ System.out.println(Thread.currentThread().getName() + "-----" + i); } } public static void main(String[] args) { Threadtest t1 = new Threadtest("執行緒1"); Threadtest t2 = new Threadtest("執行緒2"); t1.start(); t2.start(); } }
2.2 實現Runnable介面,重寫run()
public class Runnabletest implements Runnable { @Override public void run() { for(int i = 0;i < 100;i++){ System.out.println(Thread.currentThread().getName() + ":::" + i); } } public static void main(String[] args) { Thread t1 = new Thread(new Runnabletest(),"執行緒1"); Thread t2 = new Thread(new Runnabletest(),"執行緒2"); t1.start(); t2.start(); } }
// 上面兩種方法都是執行緒交替進行 執行緒1:::0 執行緒1:::1 執行緒1:::2 執行緒1:::3 執行緒1:::4 執行緒2:::0 執行緒1:::5 執行緒1:::6 執行緒1:::7 執行緒1:::8 執行緒1:::9 執行緒1:::10 執行緒2:::1
3.執行緒的方法
3.1 命名 getName()
- 該構造函數里的init方法的第三個參數是執行緒名
- 第三個參數是個函數,該函數同步地維護了threadInitNumber,是一個數字
- 可想而知,執行緒名字是
Thread-Number
,eg:Thread-0
- 從2.1/2.2可知構造方法裡面提供了命名執行緒的方式
/** * Allocates a new {@code Thread} object. This constructor has the same * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread} * {@code (null, null, gname)}, where {@code gname} is a newly generated * name. Automatically generated names are of the form * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer. */ public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } /* For autonumbering anonymous threads. */ private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; }
- 也可以通過 setName(String name) 來命名,不過該方法調用native方法,筆者水平有限不做深究
Thread thread = new Thread(() -> { System.out.println(Thread.currentThread().getName()); }); thread.setName("新執行緒名");
新執行緒名
3.1 守護執行緒(setDaemon)
- 守護進程是為其他執行緒服務的執行緒,存在於後台,一旦有執行緒就存在,執行緒全部消失而結束,eg: 垃圾回收執行緒
- 守護執行緒中產生的新執行緒也是守護執行緒
- 需要在進程啟動前調用Thread.setDaemon(true),會用native方法檢測,非法則拋出異常
public final void setDaemon(boolean on) { checkAccess(); if (isAlive()) { throw new IllegalThreadStateException(); } daemon = on; //設置守護執行緒為真 } /* Whether or not the thread is a daemon thread. */ // private boolean daemon = false;
3.2 優先順序(setPriority)
- 設置獲取CPU時間片的幾率,分0—10等級,默認為5
// MAX_PRIORITY 最大級別10 // MIN_PRIORITY 最小0 // NORM_PRIORITY 普通5 public final void setPriority(int newPriority) { ThreadGroup g; //執行緒組 checkAccess(); if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } if((g = getThreadGroup()) != null) { if (newPriority > g.getMaxPriority()) { newPriority = g.getMaxPriority(); } setPriority0(priority = newPriority); //設置級別,後面遇到native方法不說明了 } }
public static void main(String[] args) { Threadtest t1 = new Threadtest("執行緒1"); Threadtest t2 = new Threadtest("執行緒2"); //設置優先順序 t1.setPriority(10); t1.start(); t2.start(); }
執行緒1:::97 執行緒1:::98 執行緒1:::99 執行緒2:::3 執行緒2:::4 執行緒2:::5
在前面實例中調用該函數,發現t1執行緒cpu執行時間片多於t2執行緒,t1完成了t2還在開頭
3.3 sleep
- 讓該執行緒休眠,不釋放鎖
- 結束重回就緒狀態
- t1.sleep(),不是t1睡眠,而是當前執行緒睡眠
- 哪個執行緒調用sleep方法,哪個執行緒就睡眠
public class Threadtest extends Thread { //設置名字 public Threadtest(String name) { super(name); } //重寫方法 public void run(){ for(int i=0; i < 100;i++){ System.out.println(Thread.currentThread().getName() + "--" + i); } } public static void main(String[] args) throws InterruptedException { Threadtest t2 = new Threadtest("執行緒2"); t2.start(); t2.sleep(10000); System.out.println("========="); } }
執行緒2--96 執行緒2--97 執行緒2--98 執行緒2--99 //10秒後,main執行緒睡眠 =======
3.4 join
- 使當前執行緒停下來等待,直到調用join()的執行緒結束,才恢復執行
- 它可以使得執行緒之間的並行執行變為串列執行
- 在start之後才執行的
- 底層還是調用wait方法
public class Threadtest extends Thread { //設置名字 public Threadtest(String name) { super(name); } //重寫方法 public void run(){ for(int i=0; i < 100;i++){ System.out.println(Thread.currentThread().getName() + "--" + i); } } public static void main(String[] args) throws InterruptedException { Threadtest t1 = new Threadtest("執行緒1"); Threadtest t2 = new Threadtest("執行緒2"); t2.setPriority(10); t1.start(); t1.join(); //main執行緒停下來,等t1執行完才繼續往下執行,所以先輸入t1,再輸出t2 t2.start(); } }
3.5 wait和notify
- wait使當前執行緒掛起,notify隨機喚醒一個同享對象鎖的執行緒,notifyAll喚醒所有
- wait必須在同步程式碼塊或同步方法中調用,先要有鎖才能釋放鎖
- wait方法釋放鎖,並處於阻塞狀態
- notify不是立即釋放鎖,要執行完同步操作才釋放鎖
- 被其他執行緒喚醒後處於就緒狀態
thread.wait(); thread.notify();
3.6 yield
使當前執行緒從運行狀態轉為就緒狀態,即可能讓別的執行緒執行,也可能自己再次執行
3.7 interrupt
- 該方法不是即時中斷執行緒,而是僅僅設置一個中斷訊號量,然後中斷操作由我們自己實現
Thread t1 = new Thread(new Runnable(()->{ // 若未發生中斷,就正常執行任務 while(!Thread.currentThread.isInterrupted()){ // 正常任務程式碼…… } // 中斷的處理程式碼…… doSomething(); }).start();
- 當執行緒在活動之前或活動期間處於阻塞狀態(正在等待、休眠或佔用狀態)且該執行緒被中斷時,拋出InterruptedException
- 阻塞執行緒調用interrupt()方法,會拋出異常,設置標誌位為false,同時該執行緒會退出阻塞
3.8 exit
退出當前執行緒(或者當run方法結束也是執行緒結束)
3.9 start和run區別
- run():僅僅是封裝被執行緒執行的程式碼,直接調用是普通方法
- start():創建執行緒,jvm調用執行緒的run()方法,所以start方法執行完,不代表run方法執行完,執行緒也不一定銷毀!
3.10 currentThread()
獲取當先運行的執行緒,Thread thread = Thread.currentThread(),屬於靜態方法