什麼是線程:
操作系統運算調度的最小單元,輕量級的進程,一個線程包含獨立的計數器、棧、局部變量等屬性,並且線程間可以共享堆內存。所謂的多線程程序,也就是處理器通過上下文切換,給不同線程分配時間片,讓人感覺線程是同時執行的。
爲什麼要使用多線程:
充分利用多核處理器
一個進程下會有多個線程,一個線程的運行會佔用一個處理器核心。現在多核cpu已經司空見慣,如果我們編程還是單線程,那麼多核cpu中只會有一個核心被使用,其他核心被閒置。爲了重複利用cpu多核資源,提高運算能力,我們使用多線程,同一時間可以在cpu的多核上運行多個線程。更快的響應體驗
比如我們請求一個頁面,如果是單線程,會挨個獲取這個頁面上的視頻、圖片、文字等等資源;如果是多線程,會並發回去這個頁面上的資源,以縮短頁面加載的時間。更快的速度,更好的用戶體驗。
線程的優先級:
線程分配到的時間片多少也就決定了線程使用處理器資源的多少,而線程優先級就是決定線程需要多或者少分配一些處理器資源的線程屬性。
線程的狀態:
NEW: 線程構建完畢,但是還沒有開始運行(還未執行thread.start());
RUNNABLE: 線程的就緒與線程的運行都稱爲RUNNABLE狀態;
BLOCKED: 阻塞狀態。線程等待獲取其他線程通過Synchronized持有的鎖時,呈現BLOCKED狀態
WAIT: 等待狀態;Lock鎖阻塞;object.wait();Thread.join();LockSupport.part()呈現的狀態
TIME_WAITING: 有超時限制的方法通常執行後呈現TIME_WAITING狀態.相當於在WAIT狀態基礎上增加了超市限制。如,Thread.Sleep(long n);;Thread.join(long n)
TERMINATE: 線程結束的狀態
Daemon線程:
Daemon線程是一種支持型線程,主要被用作程序中後臺調度以及支持性工作。當一個java虛擬機中不存在非daemon線程時,java虛擬機將會退出。
注意:無法保證Demon線程中的finally塊一定執行。
線程的構造:
線程的構建也就是初始過程,通常一個線程由其父進程創建:父進程爲子進程分配空間,子進程繼承InheritableThreadLocal、繼承了classLoader、繼承是否Demon,並且子進程獲取到了一個唯一ID
線程的中斷:
線程的一個標識位屬性,表示一個運行中的線程是否被其他線程進行了中斷。
isIntrrupted()方法:判斷是否被中斷;
Thread.interrupted():對當前線程的中斷標識位復位;
- 如果該線程已經處於終結狀態,即時該線程被中斷過,該線程對象的isInterrupted()方法返回false;
- 申明拋出InterruptedException的方法,在拋出異常之前,java虛擬機先清除中斷標識位,調用isInterrupted方法返回false;
安全終止線程:
- 通過中斷標誌;
- 通過boolean變量;
線程間的通信:
- volatile:用來修飾字段(成員變量),告知程序任何對該變量的訪問都需要從共享內存中獲取,對它的改變必須同步刷新回共享內存,保證所有線程對變量訪問的可見性;
- synchronize:修飾方法或者同步快;確保多個線程在同一時刻,只能有一個線程處於方法或者同步快中,保證了線程對變量訪問的可見性和排他性;
- 等待/通知機制:一個線程修改了一個對象的值,而另一個線程感知到了變化,進行操作,前者爲生產者,後者爲消費者。
存在的問題:
- 難以確保及時性:在睡眠中,基本不消耗處理器資源,但是如果睡得過久,就不能及時發現條件已經變化;
- 難以降低開銷:降低睡眠的時間,可能會消耗更多的處理器資源;
解決方法:等待/通知機制;
- notify():通知一個在對象上等待的線程,使其從wait()方法返回,而返回的條件是該線程獲取到了對象的鎖;
- notifyAll():通知所有等待在該對象上的鎖;
- wait():調用該方法的線程進入WAITING狀態,只有等待另外線程的通知或被中斷纔會返回,調用wait()方法後,會釋放對象的鎖;
- wait(long):超時等待一段時間,參數爲毫秒,沒有通知就超時返回;
- wait(long,int):超時時間更細粒度的控制,可以達到納秒;
等待(消費者)/通知(生產者)的經典範式:
等待方原則:
- 獲取對象的鎖;
- 如果條件不滿足,那麼調用對象的wait方法,被通知後依然要檢查條件;
- 條件滿足則執行相應的邏輯;
僞代碼:
synchronized(對象)
{
while(條件不滿足)
{
對象.wait();
}
對應的處理邏輯;
}
通知方原則:
- 獲取對象的鎖;
- 改變條件;
- 通知所有等待在對象上的線程;
僞代碼:
synchronized(對象){
改變條件
對象.notifyAll();
}