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類中有很多的方法
//此時調用的方法表示自加
//當然還有很多的方法,這就需要各位大牛自己研究了
}