volatile不能保證原子性及其解決方案

volatile不能保證原子性及其解決方案

volatile類型的變量有三個特點
1:可見性
2:不能保證原子性
3:禁止重排

2、不能保證原子性

或者說最終一致性不能得到保證,我們看如下案例

import java.util.concurrent.TimeUnit;
class MyData{//我們創建一個資源類
	volatile int number = 0;
	public void numberPlusPlus() {
		number++;
	}
}
public class VolatileDemo {	
	public static void main(String[] args) {
		MyData myData = new MyData();	
			for (int i = 1; i <=20; i++) {
				new Thread(()->{//每次循環新建一個線程,一共創建20個線程
					for (int j = 1; j <=1000; j++) {
					//每一個線程都使number自加1000次
						myData.numberPlusPlus();
					}				
				},String.valueOf(i)).start();
			}
			try {
				TimeUnit.SECONDS.sleep(3);
				//設置3秒的時長
			} catch (Exception e) {
				// TODO: handle exception
			}
			while (Thread.activeCount()>2) {
			//判斷大於兩個活躍線程,是因爲,一個Java程序,還有主線程和gc線程
				Thread.yield();
			}	
			System.out.println(Thread.currentThread().getName()+myData.number);
	}
}

很容易理解的一個程序,按照正常的想法來看20個線程,每個使number自加1000次
應該的到20000的結果
但是此程序實際運行結果則不然,基本運行的都不是20000(偶然情況下,也可以是,但是概率很小),這是因爲多線程的情況下,各個線程搶佔資源,對數據進行讀寫,有一部分是同時進行的看下圖
在這裏插入圖片描述
比如說 a線程此時拿到了number的值爲0,b線程也拿到了number的值爲0,他倆一起返回各自的內存工作區,進行自加操作,此時a線程中的number變爲1,然後返回主內存中把主內存的number的值改寫爲1。b線程中的number在自己的內存工作區中,b線程把number自加爲1,再去主內存中把number的值改寫爲1。
此時很明顯,number自加了兩次,卻最後的值爲1
這就是多線程中存在的一個問題,但是肯定萬能的程序員肯定是有解決辦法的
這裏給大家提供兩種
1:加鎖synchronized(不推薦因爲不是最優的辦法)
public ynchronized void numberPlusPlus() {
number++;
}
2:AtomicInteger類型數據
我的理解是原子integer類,這個類型的數據可以保證各個線程操作的是同一個數據,一個線程對該數據進行操作時,其他線程不能操作,這就保證的原子性或者說最終一致性
AtomicInteger atomicInteger = new AtomicInteger();
public void addAtomicInteger() {
atomicInteger.getAndIncrement();
//AtomicInteger類中有很多的方法
//此時調用的方法表示自加
//當然還有很多的方法,這就需要各位大牛自己研究了
}

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