Java_17:volatile和AtomicInteger

1. volatile

1.什麼是volatile

volatile是Java虛擬機提供的輕量級的同步機制,保證了可見性和有序性(禁止指令重排序),保證了JMM三個特性中的兩個

2.JMM-Java內存模型

Java_17:volatile和AtomicInteger
JMM的三個特性:
可見性、有序性、原子性
可見性:

線程在自己的工作內存中修改了從主內存中拷貝的共享變量副本後,並把修改後的值重新傳到主內存中進行更新。這時我們要保證其他線程第一時間也可以得到共享變量已經被修改的通知。這樣就保證了線程之間的一個可見性(因爲線程間是不能直接訪問對方的工作內存,所以可以從主內存下手)
有序性:
禁止指令重排,避免多線程的環境下,程序出現亂序執行的現象。
指令重排:計算機在執行程序時,爲了提高性能,編譯器和處理器常常回對指令做重排

Java_17:volatile和AtomicInteger
原子性:
某個線程正在做某個具體業務時,中間不可以被加塞或者被分割。需要整體完整,要麼同時成功,要麼同時失敗。

2驗證volatile的特性

2.1 可見性
package volatileTest;

/**
 * 驗證volitile的可見性
 * @author zhaomin
 * @date 2020/4/21 15:44
 */
class MyData{
    volatile int num=0;
    public void addTo60(){
        this.num=70;
    }
}
public class Test1 {
    public static void main(String[] args) {
        MyData data=new MyData();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() + "進來了。。。");
            try {
                Thread.sleep(3);//保證主線程已經得到了num=0
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            data.addTo60();
            System.out.println(Thread.currentThread().getName()+"將值改爲"+data.num);

        },"AAA").start();

        while (data.num==0){

        }
        System.out.println(Thread.currentThread().getName() + "近啦了");
            System.out.println(Thread.currentThread().getName()+"獲取num="+data.num);
    }
}

Java_17:volatile和AtomicInteger
說明:num被voalite修飾,AAA線程執行了addTo60後,將num的值改爲70,如果沒有可見性的話,主線程main是不會感受到num已經被修改了,應該會一直循環,但結果表明,main並沒有一直在循環體中,而是可以得到70這個值,所以表明,volatile修飾了變量,使其具有可見性

2.2.驗證volatile不保證原子性
package volitileTest;
/**
 * 驗證volatile不保證原子性
 * @author zhaomin
 * @date 2020/4/21 16:25
 */
class Num{
    volatile int num=0;
    //20個線程對num進行加1操作,每個線程執行1000次,理論上應該爲20000
    public void numAdd(){
        num++;
    }
}
public class Test2 {
    public static void main(String[] args) {
        Num obj=new Num();
        for(int i=1;i<=20;i++) {
            new Thread(()->{
              for(int j=0;j<1000;j++){
                  obj.numAdd();
              }
            },String.valueOf(i)).start();
        }
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"獲取結果爲:"+obj.num);
    }
}

Java_17:volatile和AtomicInteger
說明:理論值應該是20000,但實際結果小於20000.
爲什麼不能保證原子性,因爲沒有鎖,線程會進行爭搶,不能及時將修改後的值寫回主內存

2.3 volatile實現禁止指令重排序

Java_17:volatile和AtomicInteger

3.使用AtomicInteger解決volatile的不能實現原子性的問題

package volitileTest;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 驗證volatile不保證原子性
 * 解決不保證原子性的問題--AtomicInteger
 * @author zhaomin
 * @date 2020/4/21 16:25
 */
class Num{
    volatile int num=0;
    //20個線程對num進行加1操作,理論上應該爲20000
    public void numAdd(){
        num++;
    }

    AtomicInteger atomicInteger=new AtomicInteger();
    public void myAtomicAdd(){
        atomicInteger.getAndIncrement();//每次加1
    }
}
public class Test2 {
    public static void main(String[] args) {
        Num obj=new Num();
        for(int i=1;i<=20;i++) {
            new Thread(()->{
              for(int j=0;j<1000;j++){
                  obj.numAdd();
                  obj.myAtomicAdd();
              }
            },String.valueOf(i)).start();
        }
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"獲取結果爲:"+obj.num);
        System.out.println(Thread.currentThread().getName()+"獲取結果爲:"+obj.atomicInteger);
    }
}

Java_17:volatile和AtomicInteger
說明:可以看到使用了AtomicInteger後,得到的結果與預期相符

3.2 AtomicInteger的方法說明:

1.incermentAndGet()---相當於++i,先加1再返回

Java_17:volatile和AtomicInteger
2.getAndIncrement()--相當於i++,先返回再加1
Java_17:volatile和AtomicInteger
3.相同點,內部都調用了unsafe類的getAndAddInt()方法
Java_17:volatile和AtomicInteger
可以看到爲什麼AtomicInteger能實現原子性,因爲原理是CAS

3.3 CAS

CAS=Compare and Set
CAS是指,在這個操作中,如果AtomicInteger的當前值是prev,那麼就更新爲一個預期值(這裏預期值是當前值加1),返回true。如果AtomicInteger的當前值不是prev,就什麼也不幹,返回false。通過CAS操作並配合do ... while循環,即使其他線程修改了AtomicInteger的值,最終的結果也是正確的。

3.4 應用

使用java.util.concurrent.atomic提供的原子操作可以簡化多線程編程:

1.原子操作實現了無鎖的線程安全;

2.適用於計數器,累加器等。

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