Java多線程的基本知識
1、進程與線程
進程是指一個程序的執行過程,持有資源和線程
線程是系統中最小的執行單元,一個進程可以有多個線程,線程共享進程資源,具有同步(線程的協作)與互斥(資源的爭搶)
例如:我們一個班級當做一個進程,班級裏面的學生就是線程,裏面的學習工具就是資源,學生們的相互協作與競爭就是線程之間的同步與互斥
2、java對線程的支持
Thead類及Runnable接口
Runnable用來定義任務 Thead控制任務執行
Runnable可以避免Thread方式由於java單繼承的特性帶來的缺陷,可以被多個線程(Thread實例)共享,適合於多個線程處理同一個資源的情況。
class MyThread extendsThread{ @Override public void run() { } } MyThread mt = new MyThread(); mt.start();
lass MyThread implements Runnable {
public void run() {
}
}
MyThread myThread = new MyThread();
Thread td = new Thread(myThread);
td.start();
3、線程的生命周期
其中阻塞事件包括:sleep、 yield、 join、wait
阻塞解除:等待或休眠時間到了 notify notifyAll
用戶線程:運行在前臺,執行具體的任務
守護線程:運行在後臺,為其他前臺線程服務 當所有的用戶線程都結束,守護線程會隨jvm一起結束
應用:數據庫連接池中的監測線程、jvm虛擬機啟動後的監測線程、 垃圾回收線程
Thread.setDaemon(true)將一個線程設置為守護線程,在start()調用前設置,否則會拋異常在守護線程中產生的新線程也是守護線程不是所有的任務都可以分配給守護線程執行,比如:讀寫操作或計算邏輯
jstack是java虛擬機自帶的一種堆棧跟蹤工具,作用是生成jvm當前時刻線程的快照,幫助定位程序出現問題的原因
線程的終止:線程的run方法執行完成是線程自動終止,線程的stop會使線程突然停止,導致業務流程中斷,影響數據庫事務的控制及資源的清理工作,正確的停止方法是使用退出標誌。
interrupt方法為Thread內部實現的一個中斷線程的標誌,isInterrupt方法可以判斷當前線程是否有中斷標誌,但線程中斷後,如果當前線程調用了線程的阻塞方法,將會拋出一個中斷異常,且中斷標誌會被清除,導致線程無法停止;
中斷只是暫停線程的執行,保留運行環境,讓cpu能去執行相應的io中斷程序,之後再回來繼續執行。
4、線程之間的交互
爭用條件:當多個線程同時共享一份數據的時候,如果每個線程都嘗試操作數據,會使數據被破壞,從而先形成爭用條件
互斥:synchronized 通過鎖的概念同一時間只有一個線程獲取操作權限
同步:
Object.wait 是線程等待釋放鎖
Object.notify 喚起隨機喚醒某一個等待線程
Object.notifyAll 喚起其他所有等待線程
5、線程的可見性
可見性:一個線程對共享變量值的修改,能及時的被其他線程看到;
共享變量:如果一個變量在多個線程的工作內存中都存在副本,那麽這個變量就是這幾個線程的共享變量。
指令重排:jvm會在編譯代碼生成程序指令的時候,為了達到對執行效率進行優化的目的,會對代碼中指令的順序進行重排,當必須滿足單線程中指令重排後的執行結果不會發生變化
在進程中所有的變量都存在堆內存中,共所有的線程共享,每個線程有自己的棧內存,相互獨立,線程中工作內存的變量的傳遞只能通過主內存來完成。
想要共享變量對所有的線程可見,必須滿足:
- 線程工作內存中更新共享變量必須及時刷新到主內存中
- 主內存的最新的共享變量能及時更新到線程的工作內存中
synchronized實現原理:
線程解鎖前,必須把共享變量的最新值刷新到主內存中
線程加鎖前,會清空工作內存中的共享變量的值,從而使用共享變量時需要從主內存中讀取最新的值(註意加鎖和解鎖需要是同一把鎖)
volatile 實現原理:
當一個變量被volatile
修飾時,任何線程對它的寫操作都會立即刷新到主內存中,並且會強制讓緩存了該變量的線程中的數據清空,必須從主內存重新讀取最新數據
特性:保證變量在線程之間的可見性;阻止編譯時和運行時的指令重排
具體實現:
1.在每個volatile寫操作前插入StoreStore屏障,在寫操作後插入StoreLoad屏障。
2.在每個volatile讀操作前插入LoadLoad屏障,在讀操作後插入LoadStore屏障。
內存屏障共分為四種類型:
LoadLoad屏障:
抽象場景:Load1; LoadLoad; Load2
Load1 和 Load2 代表兩條讀取指令。在Load2要讀取的數據被訪問前,保證Load1要讀取的數據被讀取完畢。
StoreStore屏障:
抽象場景:Store1; StoreStore; Store2
Store1 和 Store2代表兩條寫入指令。在Store2寫入執行前,保證Store1的寫入操作對其它處理器可見
LoadStore屏障:
抽象場景:Load1; LoadStore; Store2
在Store2被寫入前,保證Load1要讀取的數據被讀取完畢。
StoreLoad屏障:
抽象場景:Store1; StoreLoad; Load2
在Load2讀取操作執行前,保證Store1的寫入對所有處理器可見。StoreLoad屏障的開銷是四種屏障中最大的。
Java多線程的基本知識