1. 程式人生 > >多執行緒(1):繼承Thread類和實現Runnable介面

多執行緒(1):繼承Thread類和實現Runnable介面

多執行緒的兩種實現方法:

1.繼承Thread類

    繼承Thread類,重寫run()方法。建立多執行緒的時候,需要建立物件例項,然後呼叫start()方法。類物件的屬性屬於執行緒私有,執行緒之間互不影響。

public class ClassExtendThread {

    public static void main(String[] args){
        Thread t1 = new Customer();
        Thread t2 = new Customer();
        t1.start();
        t2.start();
    }
}

class Customer extends Thread{

    private int account = 10;

    public void run(){
        while (true){
            if(account > 0){
                account--;
                System.out.println("執行緒:" + Thread.currentThread().getName() + ",執行結果:" + account);
            } else {
                break;
            }
        }
    }
}

    列印輸出的結果:

執行緒:Thread-0,執行結果:9
執行緒:Thread-0,執行結果:8
執行緒:Thread-0,執行結果:7
執行緒:Thread-1,執行結果:9
執行緒:Thread-1,執行結果:8
執行緒:Thread-0,執行結果:6
執行緒:Thread-0,執行結果:5
執行緒:Thread-1,執行結果:7
執行緒:Thread-0,執行結果:4
執行緒:Thread-1,執行結果:6
執行緒:Thread-0,執行結果:3
執行緒:Thread-1,執行結果:5
執行緒:Thread-0,執行結果:2
執行緒:Thread-1,執行結果:4
執行緒:Thread-0,執行結果:1
執行緒:Thread-1,執行結果:3
執行緒:Thread-0,執行結果:0
執行緒:Thread-1,執行結果:2
執行緒:Thread-1,執行結果:1
執行緒:Thread-1,執行結果:0

    執行緒Thread-0和Thread-1,各自執行10次,將屬性account的值從10減到0,相互之間無影響。

    在main方法中,兩次 new Customer(),執行緒1和執行緒2各自有一份例項副本。執行的時候,執行緒對應的棧,儲存各自的變數和物件地址的引用。

2.實現Runnable介面

    實現Runnable介面,重寫run()方法。建立多執行緒的時候,可以跟繼承Thread相同的功能,執行緒之間相互不影響。也可以是,執行緒共享資料。這裡只說執行緒執行緒共享資料。

public class ClassImplRunnable {

    public static void main(String[] args){
        Student s = new Student(5);
        Thread t1 = new Thread(s);
        Thread t2 = new Thread(s);
        Thread t3 = new Thread(s);
        Thread t4 = new Thread(s);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

class Student implements Runnable{

    private int score;

    public Student(int score){
        this.score = score;
    }

    @Override
    public void run() {
        while (true){
            if(score > 0){
                score--;
                System.out.println("執行緒:" + Thread.currentThread().getName() + ", 剩餘分數:" + score);
            } else {
                break;
            }
        }
    }
}

    列印輸出結果:

執行緒:Thread-3, 剩餘分數:8
執行緒:Thread-1, 剩餘分數:7
執行緒:Thread-0, 剩餘分數:8
執行緒:Thread-0, 剩餘分數:4
執行緒:Thread-0, 剩餘分數:2
執行緒:Thread-0, 剩餘分數:1
執行緒:Thread-0, 剩餘分數:0
執行緒:Thread-1, 剩餘分數:5
執行緒:Thread-3, 剩餘分數:6
執行緒:Thread-2, 剩餘分數:3

    執行緒Thread-0、Thread-1、Thread-2、Thread-3,共享一份物件例項,將score的值從10減到0。

    在main方法中,只有一次new Customer()。建立四個執行緒,傳遞的引數為Customer物件,此時四個執行緒共同擁有這個例項物件,執行的時候,相互之間會有影響(涉及到執行緒安全的問題)。

    從執行的結果可以看到,8出現了兩次,而且執行緒之前無序。這就是多執行緒之間的資料共享問題。因為執行緒是併發執行的,同一時間,會有多個執行緒共同操作資料。如果當前score的值是9,Thread-3在執行的時候,還未來得及將變更的資料寫會記憶體,Thread-0拿到了未變更的資料(score=9),也進行操作,就會出現兩個8。

    解決辦法,在run方法上synchronized關鍵字,保證同一時間,只有一個執行緒能操作變更。

public class ClassImplRunnable {

    public static void main(String[] args){
        Student s = new Student(10);
        Thread t1 = new Thread(s);
        Thread t2 = new Thread(s);
        Thread t3 = new Thread(s);
        Thread t4 = new Thread(s);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

class Student implements Runnable{

    private int score;

    public Student(int score){
        this.score = score;
    }

    @Override
    public synchronized void run() {
        while (true){
            if(score > 0){
                score--;
                System.out.println("執行緒:" + Thread.currentThread().getName() + ", 剩餘分數:" + score);
            } else {
                break;
            }
        }
    }
}

    列印輸出結果:

執行緒:Thread-1, 剩餘分數:9
執行緒:Thread-1, 剩餘分數:8
執行緒:Thread-1, 剩餘分數:7
執行緒:Thread-1, 剩餘分數:6
執行緒:Thread-1, 剩餘分數:5
執行緒:Thread-1, 剩餘分數:4
執行緒:Thread-1, 剩餘分數:3
執行緒:Thread-1, 剩餘分數:2
執行緒:Thread-1, 剩餘分數:1
執行緒:Thread-1, 剩餘分數:0

    關於鎖的使用,在這裡就不詳細描述。在後續的文章中,會詳細講解。