要解釋線程,就必須明白什麼是進程。
進程是指運行中的應用程序,每個進程都有自己獨立的地址空間(內存空間),比如用戶點擊桌面的IE瀏覽器,就啓動了一個進程,操作系統就會爲該進程分配獨立的地址空間。當用戶再次點擊左面的IE瀏覽器,又啓動了一個進程,操作系統將爲新的進程分配新的獨立的地址空間。目前操作系統都支持多進程。
要點:用戶每啓動一個進程,操作系統就會爲該進程分配一個獨立的內存空間。
線程(線程是進程內的執行單元):
是進程中的一個實體,是被系統獨立調度和分派的基本單位,線程自己不擁有系統資源,只擁有一點在運行中必不可少的資源,但它可與同屬一個進程的其它線程共享進程所擁有的全部資源。一個線程可以創建和撤消另一個線程,同一進程中的多個線程之間可以併發執行。線程有就緒、阻塞和運行三種基本狀態。
命令:
列出進程:jps
- 實現Runnable接口和繼承Thread可以得到一個線程類,new一個實例出來,線程就進入了初始狀態
- 可運行狀態只是說你資格運行,調度程序沒有挑選到你,你就永遠是可運行狀態。
- 調用線程的start()方法,此線程進入可運行狀態。
- 當前線程sleep()方法結束,其他線程join()結束,等待用戶輸入完畢,某個線程拿到對象鎖,這些線程也將進入可運行狀態。
- 當前線程時間片用完了,調用當前線程的yield()方法,當前線程進入可運行狀態。
- 鎖池裏的線程拿到對象鎖後,進入可運行狀態。
- 線程調度程序從可運行池中選擇一個線程作爲當前線程時線程所處的狀態。這也是線程進入運行狀態的唯一一種方式。
- 當線程的run()方法完成時,或者主線程的main()方法完成時,我們就認爲它死去。這個線程對象也許是活的,但是,它已經不是一個單獨執行的線程。線程一旦死亡,就不能復生。
- 在一個死去的線程上調用start()方法,會拋出java.lang.IllegalThreadStateException異常。
- 當前線程T調用Thread.sleep()方法,當前線程進入阻塞狀態。
- 運行在當前線程裏的其它線程t2調用join()方法,當前線程進入阻塞狀態。
- 等待用戶輸入的時候,當前線程進入阻塞狀態。
- 調用obj的wait(), notify()方法前,必須獲得obj鎖,也就是必須寫在synchronized(obj) 代碼段內。
- 與等待隊列相關的步驟和圖
- 線程1獲取對象A的鎖,正在使用對象A。
- 線程1調用對象A的wait()方法。
- 線程1釋放對象A的鎖,並馬上進入等待隊列。
- 鎖池裏面的對象爭搶對象A的鎖。
- 線程5獲得對象A的鎖,進入synchronized塊,使用對象A。
- 線程5調用對象A的notifyAll()方法,喚醒所有線程,所有線程進入鎖池。||||| 線程5調用對象A的notify()方法,喚醒一個線程,不知道會喚醒誰,被喚醒的那個線程進入鎖池。
- notifyAll()方法所在synchronized結束,線程5釋放對象A的鎖。
- 鎖池裏面的線程爭搶對象鎖,但線程1什麼時候能搶到就不知道了。||||| 原本鎖池+第6步被喚醒的線程一起爭搶對象鎖。
- 當前線程想調用對象A的同步方法時,發現對象A的鎖被別的線程佔有,此時當前線程進入鎖池狀態。簡言之,鎖池裏面放的都是想爭奪對象鎖的線程。
- 當一個線程1被另外一個線程2喚醒時,1線程進入鎖池狀態,去爭奪對象鎖。
- 鎖池是在同步的環境下才有的概念,一個對象對應一個鎖池。
1)繼承Thread類創建線程
2)實現Runnable接口創建線程
通過繼承Thread類來創建並啓動多線程的一般步驟如下
1】d定義Thread類的子類,並重寫該類的run()方法,該方法的方法體就是線程需要完成的任務,run()方法也稱爲線程執行體。
2】創建Thread子類的實例,也就是創建了線程對象
3】啓動線程,即調用線程的start()方法
代碼實例
public class MyThread extends Thread{//繼承Thread類
public void run(){
//重寫run方法
}
}
public class Main {
public static void main(String[] args){
new MyThread().start();//創建並啓動線程
}
}
通過實現Runnable接口創建並啓動線程一般步驟如下:
1】定義Runnable接口的實現類,一樣要重寫run()方法,這個run()方法和Thread中的run()方法一樣是線程的執行體
2】創建Runnable實現類的實例,並用這個實例作爲Thread的target來創建Thread對象,這個Thread對象纔是真正的線程對象
3】第三部依然是通過調用線程對象的start()方法來啓動線程
代碼實例:
public class MyThread2 implements Runnable {//實現Runnable接口
public void run(){
//重寫run方法
}
}
public class Main {
public static void main(String[] args){
//創建並啓動線程
MyThread2 myThread=new MyThread2();
Thread thread=new Thread(myThread);
thread().start();
//或者 new Thread(new MyThread2()).start();
}
}
Thread.stop() 不推薦使用,它會立即釋放所有monitor(對象或鎖),會導致多線程的數據的不一致性,例如寫進程突然終止,讀進程可能會讀到剛纔還沒有寫完的“殘破”的數據。
public void Thread.interrupt() // 中斷線程
public boolean Thread.isInterrupted() // 判斷是否被中斷
public static boolean Thread.interrupted() // 判斷是否被中斷,並清除當前中斷狀態
直接打斷,並不能夠把該線程中斷,因爲一直在循環中:
public void run(){
while(true){
Thread.yield();
}
}
t1.interrupt();
優雅的方法,如果發現有人“打招呼”,就會跳出循環:
public void run(){
while(true){
if(Thread.currentThread().isInterrputed()){
System.out.println("interrupted");
break;
}
Thread.yield();
}}
設置睡眠,如果拋出異常後會清除中斷標記位 ,需要設置中斷狀態
public static native void sleep(long millis) throws InterruptedException
public void run(){
while(true){
if(Thread.currentThread().isInterrupted()){
System.out.println("Interruted!");
break;
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("Interruted When Sleep");
//設置中斷狀態,拋出異常後會清除中斷標記位
Thread.currentThread().interrupt();
}
Thread.yield();
}
}
(4)掛起(suspend)和繼續執行(resume)線程
– suspend()不會釋放鎖
– 如果加鎖發生在resume()之前 ,則死鎖發生