多執行緒入門總結
要更好的理解多執行緒的話,必須要把多執行緒的生命週期搞懂。
多執行緒的生命週期
1.新建(New):執行緒建立以後就處於新建狀態,Thread t = new Thread();
2.就緒(Runnable):當執行緒呼叫start()方法就進入就緒狀態,執行緒進入就緒狀態後不會立即執行,而是會等待CPU來呼叫。
3.執行(Running):當CPU呼叫就緒的執行緒就進入執行狀態了。
4.阻塞(Blocked):處於執行狀態的執行緒由於某種原因,暫時放棄對CPU的使用權,此時進入阻塞狀態,直到其進入就緒狀態執行緒才能重新呼叫進入到執行狀態。
根據阻塞原因的不同,阻塞狀態可分為三種:
1.等待阻塞:執行狀態中的執行緒執行wait()方法,使本執行緒進入到等待阻塞狀態。
2.同步阻塞:執行緒在獲取synchronized同步鎖失敗(因為鎖被其它執行緒所佔用),它會進入同步阻塞狀態;
3.其他阻塞:通過呼叫執行緒的sleep()或join()或發出了I/O請求時,執行緒會進入到阻塞狀態。當sleep()狀態超時、join()等待執行緒終止或者超時、或者I/O處理完畢時,執行緒重新轉入就緒狀態。
5.死亡(Dead):執行緒執行完了或者因異常退出了run()方法,該執行緒結束生命週期。
執行緒的建立的常用兩種方式
1.繼承Thread類,重寫該類的run()方法。
public class MyThread extends Thread { private String name; private static int num = 10000; public MyThread(String name) { this.name = name; } public void run() { for (int i = 1; i > 0; i++) { if(num>0){ num = num-1; }else{ break; } System.out.println("程式" + name + "在執行,當前num值"+num); } } public static void main(String[] args) { Thread thread1 = new MyThread("MyThread1");//新建執行緒MyThread,執行緒進入新建狀態 Thread thread2 = new MyThread("MyThread2"); thread1.start();//執行緒呼叫start()進入就緒狀態等待CPU呼叫 thread2.start(); } }
2.實現Runnable介面
public class MyRunnable implements Runnable { private String name; private static int num = 10000; public MyRunnable(String name) { this.name = name; } @Override public void run() { for (int i = 1; i > 0; i++) { if (num > 0) { num = num - 1; System.out.println("程式" + name + "在執行,當前num值" + num); } else { break; } } } public static void main(String[] args) { Runnable runnable1 = new MyRunnable("MyRunnable1"); Runnable runnable2 = new MyRunnable("MyRunnable2"); Thread thread1 = new Thread(runnable1); //新建執行緒MyRunnable1,執行緒進入新建狀態 Thread thread2 = new Thread(runnable2); thread1.start(); //呼叫start方法,進入就緒狀態 thread2.start(); } }
3.Thread和Runnable的區別
這裡用最經典的買票舉例
先貼程式碼吧
public class MyThread extends Thread { private int num = 5; private String name; public MyThread(String name) { this.name = name; } public void run() { try { for (int i = 1; i > 0; i++) { if (num > 0) { num--; } else { break; } sleep(100); System.out.println(name + "視窗,當前票數" + num); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { MyThread t1 = new MyThread("A"); MyThread t2 = new MyThread("B"); t1.start(); t2.start(); } }
用繼承Thread方式執行結果可以看到車票被重複賣出了。再來看看實現Runnable介面的方式
public class MyRunnable implements Runnable { private int num = 5; @Override public void run() { for (int i = 1; i > 0; i++) { if (num > 0) { num--; System.out.println(Thread.currentThread().getName() + "視窗,當前票數" + num); } else { break; } } } public static void main(String[] args) { Runnable runnable = new MyRunnable(); new Thread(runnable, "A").start(); new Thread(runnable, "B").start(); } }
相信你已經可以看出區別了,但是多執行幾次你會發現
這是因為執行緒執行會出現搶佔資源的情況導致不能同步,所以要加上synchronized關鍵字。
總結
實現Runnable介面比繼承Thread類所具有的優勢:
(1):適合多個相同的程式程式碼的執行緒去處理同一個資源。
(2):可以避免java中的單繼承的限制。
(3):增加程式的健壯性,程式碼可以被多個執行緒共享,程式碼和資料獨立。
(4):你只要記住實際開發中一般都用實現Runnable的方式就完事了。