多執行緒(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
關於鎖的使用,在這裡就不詳細描述。在後續的文章中,會詳細講解。