一、線程安全性
1、當多個線程訪問某個類時,不管運行時環境採用何種調度方式或者這些進程將如何交替執行,並且在主調代碼中,不需要任何額外的同步或協同,這個類都能表現出正確的行爲,那麼就稱這個類時線程安全的。
2、原子性
提供了互斥訪問,同一時刻只能有一個線程來對它進行操作
3、可見性
一個線程對主內存的修改可以及時的被其他線程觀察到。
4、有序性
一個線程觀察其他線程中的指令執行順序,由於指令重排序的存在,該觀察結果一般雜亂無序
二、可見性
1、導致共享變量在線程間不可見的原因
- 線程交叉執行
- 重排序結合線程交叉執行
- 共享變量更新後的值沒有在工作內存與主存間及時更新
2、JMM => synchronized 的兩條規定
- 線程解鎖前,必須吧共享變量的最新值刷新到主內存
- 線程加鎖時,將清空工作內存中共享變量的值,從而使用共享變量時需要從主內存中重新讀取最新的值(注意, 加鎖與解鎖時同一把鎖)
3、可見性-volatile
- 通過加入內存屏障和禁止重排序優化來實現
- 對volatile 變量寫操作時,會在寫操作後加入一條store 屏障指令,將本地內存中的共享變量值刷新到主內存。
- 對volatile 變量讀取操作時,會在讀取操作前加入一條load屏障指令,從主內存中讀取共享變量
- 可見性-volatile寫
a StoreStore屏障:禁止上面的普通寫和下面的volatile寫重排序
b StoreLoad屏障 防止上面的volatile寫與下面可能有的volatile讀/寫重排序
- 可見性-volatile讀
a 禁止下面所有普通讀操作和上面的volatile讀重排序
b 禁止下面所有的寫操作和上面的volatile 讀重排序
c 作爲一個狀態標識量
volatile boolean inited = false;
// 線程1
context = loadContext();
inited = true;
// 線程2
while(!inited) {
sleep();
}
do SomethingWithConfig(context);
4、有序性
- java 內存模型中允許編譯器和處理器對指令進行重排序,但是重排序過程不會影響單線程程序的執行,卻會影響到多線程併發執行的正確性
- volatile、synchronized 、Lock
5、有序性-Happens-before原則
- 程序次序規則: 一個線程內,按照代碼順序,書寫在前面的操作先行發生於書寫在後面的操作
- 鎖定規則: 一個unlock操作先行發生於後面對同一個鎖的lock操作
- volatile: 變量規則:對一個變量的寫操作先行發生於後面對這個變量的讀操作
- 傳遞規則: 如果操作A先行發生於操作B,而操作B又先行發生於操作C,則可以得出A先行發生於操作C
- 線程啓動規則:Thread 對象的start()方法先行發生於此線程的每一個動作
- 線程中斷規則: 對線程interrupt()方法的調用先行發生於被中斷線程的代碼檢測到中斷事件的發生
- 線程終結規則: 線程中所有的操作都先行發生於線程的終止檢測,我們可以通過Thread.join()方法結束、Thread.isAlive()的返回值手段檢測到線程已經終止執行
- 對象終結規則: 一個對象的初始化完成先行發生於他的finalize()方法的開始
三、併發的優勢與風險
1、安全性
多個線程共享數據時可能會產生於期望不相符合的結果
2、活躍性
某個操作無法繼續執行下去時,就會發生活躍性問題、死鎖、飢餓等問題
3、性能
線程過多時會使得CPU 頻繁切換,調度時間增多,同步機制,消耗過多內存
4、速度
同時處理多個請求,響應更快,複雜的操作可以分成多個進程同時處理
5、設計
程序設計在某些情況下更簡單,有更多的選擇
6、資源利用
CPU 能夠在等待IO 的時候做一些其他的事情