1. 程式人生 > >多執行緒入門總結

多執行緒入門總結

要更好的理解多執行緒的話,必須要把多執行緒的生命週期搞懂。

多執行緒的生命週期

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的方式就完事了。