1 synchronized
synchronized關鍵字可以實現一個簡單的策略來防止干擾和內存一致性錯誤,如果一個對象是對多個線程是可見的,那麼對該對象的所有讀或寫都將通過同步的方式來進行.
- synchronized 關鍵字提供了一種鎖機制,能夠確保共享變量的互斥訪問,從而防止數據不一致問題的出現.
- synchronized關鍵字包括enter monitor和exit monitor兩個jvm指令,它能夠在任何時候任何線程執行到monitor enter成功之前都必須從主內存獲取數據,而不是從緩存中.在monitor exit之後,共享變量被更新後的值會刷入主內存
- synchronized嚴格遵守java happens-before規則,一個monitor exit之前必定有一個monitor enter
1.1 synchronized用法
synchronized可以對代碼塊或方法進行修飾.
1.1.1 同步方法
1.1.2 同步代碼塊
1.2 monitor
1.2.1 monitorenter
每個對象都有一個monitor相關聯,一個monitor的lock鎖只能被一個線程在同一時間獲取,在一個線程嘗試獲取與對象關聯的monitor的所有權時會發生如下:
- 如果monitor的計數器爲0,則意味着該對象的monitor的鎖還沒有被獲得,某個線程獲取後將立即對計數器加一,從此該線程就是這個monitor的所有者了.
- 如果已經擁有該monitor的線程重入,則會導致該計數器再加1
- 如果monitor已經被其他線程所擁有,則其他線程嘗試獲取該monitor的所有權時,會被陷入阻塞狀態知道monitor計數器爲0,才能再次嘗試獲取monitor的擁有權.
1.2.2 monitorexit
釋放對monitor的擁有權,前提釋放之前線程必須擁有該線程monitor的所有權.釋放其實就是將monitor的計數器減1.如果計數器的結果爲0,則該線程不再擁有對該monitor的擁有權,通俗來講就是解鎖.
1.3 使用synchronized的注意事項
- 與monitor關聯的對象不能爲空
- synchronized作用域太大:如果synchronized作用域越大,則代表着效率越低,甚至還會喪失併發的優勢.
- 不同的monitor企圖鎖相同的方法
- 多個鎖的交叉導致死鎖
1.4 this monitor和class monitor
- 同步成員方法實際就是用了對象的this monitor
- 同步靜態方法實際就是用來類的class對象的monitor
2 程序死鎖的原因及其診斷
2.1 程序死鎖
- 交叉所可導致程序死鎖:線程A持有R1的鎖等待獲取R2的鎖,線程B持有R2的鎖等待獲取R1的鎖.
- 內存不足:當併發請求系統可用內存時,如果此時系統內存不足,可能會出現死鎖情況.
- 一問一答式的數據交換:服務端開啓某個端口,等待客戶端訪問,客戶端方位並立即等待接受響應,由於某種原因服務的錯過了客戶端的請求,仍然在等待一問一答式的數據交換,此時服務端和客戶段都在等待着對方的答應.
- 數據庫鎖:無論是數據表級別的鎖,還是行級別的鎖,比如某個線程for update語句退出了事務,其他線程訪問該數據庫時都將陷入死鎖.
- 文件鎖:某線程獲取文件鎖但是意外退出,其他讀取讀取該文件的線程也會進入死鎖直到系統釋放文件句柄.
- 死循環引起的死鎖:程序由於代碼原因,或則和對某些異常處理不正確,進入了死循環,雖然查看線程堆棧信息不會發現任何死鎖的現象,但是程序不工作,CPU佔用率又居高不下,這種死鎖叫做系統假死,也是一種最爲致命且很難排查的死鎖現象.