一個程式完全入門Java多執行緒
阿新 • • 發佈:2018-11-27
程式碼只供學習使用,實際開發中建議遵守Java開發規範,合理分包
程式碼所涉及知識點:
什麼是執行緒、Thread方法和Runnable介面的介紹及建立執行緒、執行緒的狀態和生命週期、sleep方法和join方法的使用、執行緒的優先順序、執行緒同步、執行緒間通訊(見另一篇文章,點選訪問)
package practice; /** * * @author slvayf * */ // 通過繼承Thread類的方式,可以完成多執行緒的建立,多執行緒由系統排程交叉執行 class MyThread extends Thread { // 父類引用,傳參修改執行緒名稱 public MyThread(String name) { super(name); } public void run() { for (int i = 1; i < 6; i++) { // sleep方法: // 通過sleep方法使執行緒休眠(暫停執行緒的執行1毫秒) // 但是暫停1毫秒後,執行緒並不會馬上進入執行狀態,而是進入可執行狀態(Runnable) // 執行thread類的sleep方法,多次執行程式我們可以發現: // ___“通過Runnable介面建立的執行緒”因為不休眠而“通過thread方法建立的執行緒:執行緒0、1、2”1毫秒休眠一次 // 所以“通過Runnable介面建立的執行緒”獲得CPU使用權開始執行的概率會更大 // 不過,因為“通過thread方法建立的執行緒:執行緒0”執行時雖然1毫秒休眠一次,但因為使用join方法,它是優先執行的 // “通過Runnable介面建立的執行緒”也可以使用thread類的sleep方法實現類似效果,join相同 try { Thread.sleep(1); } catch (InterruptedException e) { // 休眠中斷異常 e.printStackTrace(); } System.out.println(getName() + "正在執行第" + i + "次"); } } } // 但是繼承Thread類這種方式有一個侷限性,如果一個類已經有了自己的父類,就不可以繼承Thread類,因為java規定類是單繼承的 // 如果該類中的還有部分程式碼需要被多個執行緒同時執行,只有對該類進行額外的功能擴充套件,java就提供了一個介面Runnable // Runnable介面中定義了run方法,其實run方法的定義就是為了儲存多執行緒要執行的程式碼 // 所以,通常建立執行緒都用第二種方式,因為,實現Runnable介面可以避免單繼承的侷限性 // 而且,繼承Thread,是可以對Thread類中的方法進行子類複寫的 // 但是不需要做這個複寫動作,只為定義執行緒程式碼存放位置的話,實現Runnable介面更方便一些 // 所以,Runnable介面將執行緒要執行的任務封裝成了物件 // 其實,將多執行緒要執行的程式碼的位置單獨定義到介面中,為其他類進行功能擴充套件提供了前提 // 所以,Thread類在描述執行緒時,內部定義的run方法,也來自於Runnable介面 class PrintRunnable implements Runnable { @Override public void run() { for (int i = 1; i < 6; i++) { System.out.println(Thread.currentThread().getName() + "正在執行第" + i + "次"+ " 優先順序:" + Thread.currentThread().getPriority()); // 主執行緒和新建立執行緒預設優先順序為5 // 設定t2的優先順序為10,多次執行下來我們可以發現,在大多數情況下,t2是要比t1優先執行的,優先順序先10後5 // 但有時候t1("通過Runnable介面建立的執行緒:執行緒1")還是在t2之前執行 // 其原因是,作業系統確定程式執行優先順序,並不是只根據使用者設定的優先順序,還要綜合計算機記憶體及其它資源利用狀況,做出綜合判斷 // 比如先啟動的低優先順序的程式t1,由於所需資源和執行時間少,可能在很短時間內全部執行完成 } } } // 兩個執行緒同步(執行緒互斥)的測試類,連續兩次對100減10,每減一次輸出一次,區別如下: // ThreadSynchronization1物件未鎖定,兩個執行緒隨機執行,執行次數無法計數故由執行緒名輸入,結果為 90 90,結果異常 // ThreadSynchronization2物件鎖定(使用synchronized關鍵字),兩個執行緒順序執行,因為是順序執行,所以執行次數可以累加(k),結果為 90 80,結果正常 // synchronized關鍵字可以用在成員方法、靜態方法和程式碼塊 class ThreadSynchronization1 implements Runnable { private int i = 100, j = 10, ans; @Override public void run() { ans = this.i - j; // 執行緒休眠1秒,模擬執行緒異常 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } this.i = ans; System.out.println("未鎖定ThreadSynchronization的物件時第" + Thread.currentThread().getName() + "次減10:" + this.i); } } class ThreadSynchronization2 implements Runnable { private int i = 100, j = 10, ans, k = 1; @Override public synchronized void run() { ans = this.i - j; // 執行緒休眠1秒,模擬執行緒異常 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } this.i = ans; System.out.println("鎖定ThreadSynchronization的物件後第" + k + "次減10:" + this.i); k++; } } public class MultiThreading { public static void main(String[] args) { // 檢視主執行緒優先順序 System.out.println("--主執行緒的優先順序為:" + Thread.currentThread().getPriority()); // MyThread、PrintRunnable類的物件例項化 MyThread mt0 = new MyThread("通過thread方法建立的執行緒,使用join搶先執行:執行緒0"); MyThread mt1 = new MyThread("通過thread方法建立的執行緒,使用sleep延遲執行:執行緒1"); MyThread mt2 = new MyThread("通過thread方法建立的執行緒,使用sleep延遲執行:執行緒2"); PrintRunnable pr1 = new PrintRunnable(); PrintRunnable pr2 = new PrintRunnable(); ThreadSynchronization1 ts1 = new ThreadSynchronization1(); ThreadSynchronization2 ts2 = new ThreadSynchronization2(); // 建立Thread類物件,引數1為:基於Runnable介面實現類的物件,引數2為:執行緒名 Thread t1 = new Thread(pr1, "通過Runnable介面建立的執行緒:執行緒1"); Thread t2 = new Thread(pr2, "通過Runnable介面建立的執行緒:執行緒2"); Thread tsTest1 = new Thread(ts1, "1"); Thread tsTest2 = new Thread(ts1, "2"); Thread tsTest3 = new Thread(ts2); Thread tsTest4 = new Thread(ts2); // 啟動執行緒(Thread類的start方法),使執行緒進入可執行(Runnable)狀態 mt0.start(); // 呼叫join方法,mt0搶佔資源優先執行 // mt0.join(10); 指:mt0執行緒在優先執行的情況下執行10毫秒,不論是否執行完畢,都將讓出自己佔用的資源,退出優先狀態 try { mt0.join(); } catch (InterruptedException e) { e.printStackTrace(); } mt1.start(); mt2.start(); t1.start(); t2.start(); // 設定t2的優先順序最高,為10 t2.setPriority(10); // 啟動執行緒同步測試 tsTest1.start(); tsTest2.start(); tsTest3.start(); tsTest4.start(); } }
一個可參考的執行結果:(為方便觀察,輸出結果做了空行分隔處理)
--主執行緒的優先順序為:5 通過thread方法建立的執行緒,使用join搶先執行:執行緒0正在執行第1次 通過thread方法建立的執行緒,使用join搶先執行:執行緒0正在執行第2次 通過thread方法建立的執行緒,使用join搶先執行:執行緒0正在執行第3次 通過thread方法建立的執行緒,使用join搶先執行:執行緒0正在執行第4次 通過thread方法建立的執行緒,使用join搶先執行:執行緒0正在執行第5次 通過Runnable介面建立的執行緒:執行緒2正在執行第1次 優先順序:10 通過Runnable介面建立的執行緒:執行緒2正在執行第2次 優先順序:10 通過Runnable介面建立的執行緒:執行緒2正在執行第3次 優先順序:10 通過Runnable介面建立的執行緒:執行緒2正在執行第4次 優先順序:10 通過Runnable介面建立的執行緒:執行緒2正在執行第5次 優先順序:10 通過Runnable介面建立的執行緒:執行緒1正在執行第1次 優先順序:5 通過thread方法建立的執行緒,使用sleep延遲執行:執行緒1正在執行第1次 通過Runnable介面建立的執行緒:執行緒1正在執行第2次 優先順序:5 通過Runnable介面建立的執行緒:執行緒1正在執行第3次 優先順序:5 通過Runnable介面建立的執行緒:執行緒1正在執行第4次 優先順序:5 通過Runnable介面建立的執行緒:執行緒1正在執行第5次 優先順序:5 通過thread方法建立的執行緒,使用sleep延遲執行:執行緒1正在執行第2次 通過thread方法建立的執行緒,使用sleep延遲執行:執行緒2正在執行第1次 通過thread方法建立的執行緒,使用sleep延遲執行:執行緒2正在執行第2次 通過thread方法建立的執行緒,使用sleep延遲執行:執行緒1正在執行第3次 通過thread方法建立的執行緒,使用sleep延遲執行:執行緒2正在執行第3次 通過thread方法建立的執行緒,使用sleep延遲執行:執行緒1正在執行第4次 通過thread方法建立的執行緒,使用sleep延遲執行:執行緒1正在執行第5次 通過thread方法建立的執行緒,使用sleep延遲執行:執行緒2正在執行第4次 通過thread方法建立的執行緒,使用sleep延遲執行:執行緒2正在執行第5次 未鎖定ThreadSynchronization的物件時第1次減10:90 未鎖定ThreadSynchronization的物件時第2次減10:90 鎖定ThreadSynchronization的物件後第1次減10:90 鎖定ThreadSynchronization的物件後第2次減10:80