原子性
- 競態關係
- 複合操作
我們還是以一段代碼來理解這兩點。
public class ThreadSecStudy {
static int count = 0;
public static void main(String[] args) {
// 正確性反例
Thread add = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 50000; i++) {
count++;
System.out.println(count);
}
}
});
Thread cut = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 50000; i++) {
count--;
System.out.println(count);
}
}
});
// 加法
add.start();
// 減法
cut.start();
}
}
我們的類方法中對count的操作看似只有一行count++和count–,但是實際上它做了三步操作,獲取-修改-寫入,拆分開來可以是這樣寫。
//獲取
int tmpGet = count;
//修改
int tmpUpdate = count + 1;
//寫入
count = tmpUpdate;
那麼我們設想如果有多個線程同時對同一個變量做這三步操作,有沒有可能出現如下這種情況呢?
線程1爲加法,線程2爲減法。
- count = 0
- =》線程1加入
- 線程1:獲取操作 tmpGet = count = 0
- =》線程2加入
- 線程2:獲取操作 tmpGet = count = 0
- 線程1:修改操作 tmpUpdate = count + 1 = 1
- 線程2:修改操作 tmpUpdate = count - 1 = -1
- 線程1:寫入操作 count = tmpUpdate = 1
- 線程2:寫入操作 count = tmpUpdate = -1
很明顯-1這個結果是不對的,一加一減正確的結果應該是0纔是正確的,那麼這就出現了線程安全的問題了。
首先這個操作它屬於複合操作,它是一個可以被拆分的操作,和線程安全性中的體現原子性是恰恰相反的。
並且當一個計算的正確性取決於多個線程的交替執行時序時,就會發生競態條件。
加深理解
如果你的操作屬於複合操作,那麼在被多個線程交替執行時序時,內部可能會發生競態條件,導致計算的結果正確性得不到保證,從而導致線程安全問題。
反之,如果確保了其操作的原子性,則不會發生競態條件;而要保證原子性則要提供互斥訪問,同一時刻只能有一個線程對數據進行操作
開發中如何做
最簡單的也最常見的方式就是我們使用synchronized的時候,我們將複合操作合併成爲一個符合原子性的操作,這樣就保證了操作的原子性。
文章中出現的任何錯誤歡迎指正,共同進步!
最後做個小小廣告,有對WEB開發和網絡安全感興趣的,可以加羣一起學習和交流!
QQ:425343603