- 線程安全:數據共享時對數據讀寫出現錯誤(數據污染)。
- 出現線程安全條件:
a.多線程環境下,單線程無線程安全問題。
b.數據共享發生,無數據共享無線程安全問題。
c.對數據讀寫時,單一讀無線程安全問題(數據共享),單一寫存在線程問題。
經典售票問題
描述:現有100張(序號1-100)動物園門票出售。現有三個售票窗口進行售票。編寫一個多線程程序模擬窗口售票。
分析:
- 三個窗口出售不同的票。沒有什麼問題,不存在數據共享不存在線程安全問題。
2.三個窗口同時出售100張票。這時候就會出現問題。數據共享讀寫出現線程問題。
Java編寫程序:
按照分析第二條進行代碼編寫測試
運行程序控制臺打印出現數據污染:
錯誤分析:先理解下線程調度的方式:線程的調度方式爲搶佔式,多個線程搶佔資源,搶到資源處於運行狀態,沒搶到進入阻塞狀態。運行線程釋放資源,線程繼續搶佔。如此循環保證只有一個線程處於運行狀態。因爲線程調度和釋放資源的速度很快所以感覺到多個線程同時在執行。
右下圖分析可以看出:當一個線程執行到打印(讀數據)時失去執行權,並沒有對票數進行tiicket--操作,其他線程也進入運行狀態這個時候就會出現多個窗口出售相同票的問題。執行到最後一張票時,可能還會出現線程執行減操作,其他線程進行讀取時出現負數票情況。
線程安全解決方案
採用線程同步原理:使可能出現線程安全問題的代碼始終只能有一個在執行,並且執行完畢後其他線程才能執行其中代碼。
-
同步代碼塊:
synchronized(鎖對象){
//可能出現線程安全的代碼
}
2.同步方法:
對可能出現線程安全的代碼使用同步方法
修飾詞 synchronized 返回值 方法名(參數){
//可能出現線程安全的代碼
}
3.使用Lock鎖(java.util.concurrent.locks.Lock):
a.創建鎖對象(新建Lock接口的實現類 例如:Lock lock = new ReentrantLock(); )
b.在可能出現線程安全的代碼前獲取鎖
c.在可能出現線程安全的代碼後釋放鎖