Java知識總結之線程安全與數據同步 1 synchronized 2 程序死鎖的原因及其診斷

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佔用率又居高不下,這種死鎖叫做系統假死,也是一種最爲致命且很難排查的死鎖現象.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章