概述
面向過程的編程思想
站在計算機的角度去抽象問題和解決問題,把數據和過程分別作爲獨立的部分來考慮,數據代表問題空間的中的客體,程序代碼則用於處理這些數據
面向對象的編程思想
站在現實世界的角度去抽象和解決問題,把數據和行爲都看作是對象的一部分
線程安全
線程安全的定義:
當多個線程訪問一個對象時,如果不用考慮這些線程在運行時環境下的調度和交替執行,也不需要進行額外的同步,或者在調用方進行任何其他的協調操作,調用這個對象的行爲都可以獲得正確的結果,那麼這個對象是線程安全的
出自<<Java Concurrency In Practice>> Brian Goetz
線程的安全與否是通過在多線程環境中對共享數據的操作來判斷的
對於這些共享數據,有以下5類,安全程序從強減弱
- 不可變
不可變的對象一定是線程安全的。
說到不可變,在java中第一反應是final關鍵字。用來聲明對象在正確構造後(沒有發生this引用逃逸的情況)不能被修改。
this引用逃逸的幾種情況是:
在構造函數中創建內部,
在構造函數中就把這個內部類給發佈了出去,
在構造函數中啓動一個線程
ps:final也可以用來保持可見性。
java api符合不可變要求的其它類型有string,enum, Long, Double, BigInteger, BigDecimal
- 絕對線程安全
- 相對線程安全
- 線程兼容
- 線程對立
無論調用端是否採取了同步措施,都無法在多線程環境中併發使用的代碼
如Thread中的suspend() ,resume()
共享數據類型 | 調用方 | 調用對象 |
---|---|---|
不可變 | 不可變 | 不可變 |
絕對線程安全 | 線程安全 | 線程安全 |
相對線程安全 | 不需要線程安全 | 線程安全 |
線程兼容 | 需要線程安全 | 非線程安全 |
線程對立 | 線程不安全 | 線程不安全 |
如何實現線程安全?
- 互斥同步
悲觀併發策略
保證在同一時刻只能被一個線程使用 臨界區,互斥量和信號量 都是主要的實現方式。
如synchronized和ReentrantLock關鍵字 一個是原生語法層面的互斥鎖,一個是api層面的互斥鎖。
reentrantLock的高級特性:等待可中斷,可實現公平鎖,以及鎖可以綁定多個條件。
jdk1.6之後兩者的性能基本持平,提倡如果synchronized能實現需求的情況下,優先使用synchronized
- 非阻塞同步
基於衝突檢測的樂觀的併發策略
就是先進行操作,如果沒有其他的線程爭用共享數據,那操作就成功了,如果有爭用,就用補償措施 如重試,直到成功爲止
- 無同步方案
鎖優化
- 自旋鎖與自適應鎖
自旋 ,因爲共享數據鎖定狀態只會持續很短的時間。爲了減少線程下上文切換帶來的性能損耗,讓當前請求線程等待,並重試請求獲取鎖。
如何開啓?
-XX:+UseSpinning 1.6之後是默認開啓的
自旋的默認次數是10次
自適應的自旋鎖
自旋重試的間隔時間及次數是由當前鎖的狀態來決定的。
個人理解:本質就是重試
- 鎖消除
鎖消除是指虛擬機在運行時,對一些代碼上要求同步,但是被檢測到不可能存在共享數據競爭的鎖進行消除。
判斷依據來源於:逃逸分析的數據支持
個人理解:其實也就是判斷當前的數據是否是線程私有的
- 鎖粗化
如果虛擬機探測到有一系列的操作都是對同一個對象進行加鎖解鎖,會把加鎖同步的範圍擴展到整個操作序列的外面
- 輕量級鎖
- 偏向鎖