參考 https://mp.weixin.qq.com/s/gBZcogHizremZoJRvd8fHQ
線程的幾個方法
- run()。非靜態
- start()。非靜態
- join()。非靜態。如果線程 A 調用了線程 B 的 join() 方法,那線程 A 會進入等待狀態,直到線程 B 運行結束。
- currentThread()。靜態
- yield()。靜態。降低當前線程優先級,調用該方法就像是是對線程調度器說:“如果其他線程要處理器資源,那就給它們,否則我繼續用”。
- sleep()。靜態
線程的生命週期
JVM內存模型
JAVA運行時的數據區域可以包括:
方法區。方法區存放類的信息(包括類的字節碼,類的結構)、常量、靜態變量等。字符串常量池就是在方法區中。
堆區。主要存放數組和對象,包括持久代,年老代,初生代等
虛擬機棧。線程獨佔。存放局部變量表(基礎數據和引用)
本地方法棧。線程獨佔。存放native相關
程序計數器(線程寄存器)。線程獨佔。保存線程執行指令
JAVA內存模型
這幅圖是區分於JVM內存模式。後者是從功能上區分內存模型,前者是從架構考慮,高速cache的存在是爲了加速數據讀寫,而不同線程之間對內存的讀寫就涉及到了多線程併發工作的問題,從而產生了競態這一說法。
線程安全性問題(競態)
- 原子性。操作不可分割。其他線程不會參與
- 可見性。某一線程改變共享變量後需要同步到其他線程
- 有序性。保證操作的有序性。重排序(Reordering)處理器和編譯器是對代碼做的一種優化,它可以在不影響單線程程序正確性的情況下提升程序的性能,但是它會對多線程程序的正確性產生影響,導致線程安全問題。
實現線程安全(鎖)
要實現線程安全就需要保證上面的三點。常見的實現線程安全的辦法是使用鎖和原子類型,而鎖可分爲內部鎖(synchronized)、顯式鎖、讀寫鎖、輕量級鎖(volatile)四種
- 內部鎖(synchronized)。同步方法或者代碼塊,鎖住特定類和特定對象。因爲使用 synchronized 實現的線程同步是通過監視器(monitor)來實現的,所以內部鎖也叫監視器鎖。不需要手動釋放鎖如(unlock),可以自動解鎖。非公平鎖
- 顯示鎖,實現了Lock接口。可重入,鎖多次;需要手動獲取和釋放,允許調整鎖策略成功公平/非公平鎖。
- 讀寫鎖。
- volatile 關鍵字
讀線程執行的加載屏障和寫線程執行的存儲屏障配合在一起,能讓寫線程對 volatile 變量的寫操作對讀線程可見,從而保證了可見性;volatile 能禁止指令重排序,也就是使用 volatile 能保證操作的有序性;在原子性方面,對於 long/double 型變量,volatile 能保證讀寫操作的原子型。
對於非 long/double 型變量,volatile 只能保證寫操作的原子性。
死鎖 4 個必要條件
- 互斥
- 佔有且等待
- 不可搶佔
- 循環等待