關於Java多線程安全問題

Java多線程安全

一、如何理解線程的安全與不安全

     多線程併發執行的情況下,仍能保證數據的正確性,這種現象稱爲線程安全,反之爲線程不安全。

二、導致線程不安全的因素有哪些

1.多個線程併發執行。
2.多個線程併發執行期間,需要訪問共享的數據資源,即共享對象。
3.多個線程對共享數據的操作不是原子操作。

三、如何保證併發情況下的線程安全

       線程安全問題主要解決三個問題,可見性,有序性和原子性,Java內存模型(JMM)已經解決了可見性和有序性問題,而原子性需要使用鎖來實現。

1.對共享數據的訪問進行限制,比如加鎖,【synchronized-JVM層面】【lock-JDK層面】等)
       將需要確保原子性的代碼或方法使用【synchronized】關鍵字或鎖對象進行加鎖,從而使同時只能有一個線程可以進行操作,來保證原子性。
       補充【synchronized】的一些內容,【synchronized】是基於Monitor對象實現同步的,任何對象都有監視器,存在對象的對象頭中,也可以理解爲對象的鎖,一個線程拿到【對象監視器】後,其他線程就不能對該對象進行操作,這裏比較抽象,感興趣的可以拓展下【Java對象的結構組成】,同步代碼塊採用【monitorenter】和【monitorexit】指令實現,同步方法使用【ACC_SYNCHRONIZED】標記符實現,此內容需要反編譯字節碼文件查看。JDK1.6之前,synchronized的性能非常差,JDK1.6以後,JVM對synchronized的性能進行了優化,支持自旋鎖,偏向鎖,輕量級鎖,重量級鎖。【synchronized】鎖分了四種級別,由低到高爲【無鎖狀態】【偏向鎖狀態】【輕量級鎖狀態】和【重量級鎖狀態】,這幾個狀態會隨着競爭的情況逐漸升級,鎖可以升級但不能降級,輕量級鎖不能重新變爲偏向鎖,目的是爲了提高獲得鎖和釋放鎖的效率。鎖升級的過程 ,emmm,有點麻煩,大家也可以自己去百度下~

2.基於CAS實現非阻塞同步(CAS需要CPU硬件支持,底層需要調用native方法)
       CAS(CompareAndSwap):對比並交換,需要三個主要數據,【內存地址V】【期望數據A】【需要更新的值B】,僅當預期值A和內存值V相同時,將內存值V修改爲B,可以實現非阻塞的數據操作。
       CAS的【ABA】問題,有兩條線程,T1將A換成了B,T2又將B換成了A,此時內存地址還是V沒有變化,但是已經對數據進行了操作,解決思路,每次修改更新版本號,對數據操作之前,對內存地址V和版本號兩個參數進行對比,以確保安全性。

3.取消共享,每個線程一個對象實例(基於ThreadLocal實現)
       ThreadLocal對象類似一個容器,可以給每條線程存儲一個對象實例,就像監考人給每個考生髮一張答題卡,而不是所有人搶同一個,如【SimpleDateFormat】,該類繼承自父類【DateFormat】,繼承了一個共享的【Calendar】對象,其【format】方法內部是先將傳入的【Date】對象通過【set】方法對【Canlerdr】對象進行設置,然後通過【subFormat】方法,轉換爲一個字符串進行返回,因此線程不安全。ThreadLocal內部維護了一個【ThreadLocalMap】,暫且可以把他當成HashMap來看,可以將當前ThreadLoal對象和線程需要存儲的對象實例,以K-V的形式存在ThreadLocalMap對象中,通過【get()和set()】方法存取。通過取消共享來保證併發安全。同樣線程不安全的對象有很多,如:Condition,SqlSession等。

呼 ~ 再繼續補充,不太想寫代碼了....

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章