【高併發】04 線程安全性、併發安全性、可見性

一、線程安全性

1、當多個線程訪問某個類時,不管運行時環境採用何種調度方式或者這些進程將如何交替執行,並且在主調代碼中,不需要任何額外的同步或協同,這個類都能表現出正確的行爲,那麼就稱這個類時線程安全的。

2、原子性

提供了互斥訪問,同一時刻只能有一個線程來對它進行操作

3、可見性

一個線程對主內存的修改可以及時的被其他線程觀察到。

4、有序性

一個線程觀察其他線程中的指令執行順序,由於指令重排序的存在,該觀察結果一般雜亂無序

二、可見性

1、導致共享變量在線程間不可見的原因

  1. 線程交叉執行
  1. 重排序結合線程交叉執行
  1. 共享變量更新後的值沒有在工作內存與主存間及時更新

2、JMM => synchronized 的兩條規定

  1. 線程解鎖前,必須吧共享變量的最新值刷新到主內存
  1. 線程加鎖時,將清空工作內存中共享變量的值,從而使用共享變量時需要從主內存中重新讀取最新的值(注意, 加鎖與解鎖時同一把鎖)

3、可見性-volatile

  1. 通過加入內存屏障和禁止重排序優化來實現
  1. 對volatile 變量寫操作時,會在寫操作後加入一條store 屏障指令,將本地內存中的共享變量值刷新到主內存。
  1. 對volatile 變量讀取操作時,會在讀取操作前加入一條load屏障指令,從主內存中讀取共享變量
  1. 可見性-volatile寫

a StoreStore屏障:禁止上面的普通寫和下面的volatile寫重排序

b StoreLoad屏障 防止上面的volatile寫與下面可能有的volatile讀/寫重排序

普通讀
普通寫
StoreStore屏障
volatile寫
StoreLoad屏障
  1. 可見性-volatile讀

a 禁止下面所有普通讀操作和上面的volatile讀重排序

b 禁止下面所有的寫操作和上面的volatile 讀重排序

volatile讀
LoadLoad屏障
LoadStore屏障
普通讀
普通寫

c 作爲一個狀態標識量

volatile boolean inited = false;
// 線程1
context = loadContext();
inited = true;

// 線程2
while(!inited) {
    sleep();
}
do SomethingWithConfig(context);

4、有序性

  1. java 內存模型中允許編譯器和處理器對指令進行重排序,但是重排序過程不會影響單線程程序的執行,卻會影響到多線程併發執行的正確性
  1. volatile、synchronized 、Lock

5、有序性-Happens-before原則

  1. 程序次序規則: 一個線程內,按照代碼順序,書寫在前面的操作先行發生於書寫在後面的操作
  1. 鎖定規則: 一個unlock操作先行發生於後面對同一個鎖的lock操作
  1. volatile: 變量規則:對一個變量的寫操作先行發生於後面對這個變量的讀操作
  1. 傳遞規則: 如果操作A先行發生於操作B,而操作B又先行發生於操作C,則可以得出A先行發生於操作C
  1. 線程啓動規則:Thread 對象的start()方法先行發生於此線程的每一個動作
  1. 線程中斷規則: 對線程interrupt()方法的調用先行發生於被中斷線程的代碼檢測到中斷事件的發生
  1. 線程終結規則: 線程中所有的操作都先行發生於線程的終止檢測,我們可以通過Thread.join()方法結束、Thread.isAlive()的返回值手段檢測到線程已經終止執行
  1. 對象終結規則: 一個對象的初始化完成先行發生於他的finalize()方法的開始

三、併發的優勢與風險

1、安全性

多個線程共享數據時可能會產生於期望不相符合的結果

2、活躍性

某個操作無法繼續執行下去時,就會發生活躍性問題、死鎖、飢餓等問題

3、性能

線程過多時會使得CPU 頻繁切換,調度時間增多,同步機制,消耗過多內存

4、速度

同時處理多個請求,響應更快,複雜的操作可以分成多個進程同時處理

5、設計

程序設計在某些情況下更簡單,有更多的選擇

6、資源利用

CPU 能夠在等待IO 的時候做一些其他的事情

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