原子性


2.4 原子性

概述 : 所謂的原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了執行並且不會受到任何因素的干擾而中斷,要么所有的操作都不執行,多個操作是一個不可以分割的整體。

代碼實現 :

package com.itheima.threadatom;
​
public class AtomDemo {
    public static void main(String[] args) {
        MyAtomThread atom = new MyAtomThread();
​
        for (int i = 0; i < 100; i++) {
            new Thread(atom).start();
        }
    }
}
class MyAtomThread implements Runnable {
    private volatile int count = 0; //送冰淇淋的數量
​
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //1,從共享數據中讀取數據到本線程棧中.
            //2,修改本線程棧中變量副本的值
            //3,會把本線程棧中變量副本的值賦值給共享數據.
            count++;
            System.out.println("已經送了" + count + "個冰淇淋");
        }
    }
}

 

代碼總結 : count++ 不是一個原子性操作, 他在執行的過程中,有可能被其他線程打斷

 

2.5 volatile關鍵字不能保證原子性

解決方案 : 我們可以給count++操作添加鎖,那么count++操作就是臨界區中的代碼,臨界區中的代碼一次只能被一個線程去執行,所以count++就變成了原子操作。

package com.itheima.threadatom2;
​
public class AtomDemo {
    public static void main(String[] args) {
        MyAtomThread atom = new MyAtomThread();
​
        for (int i = 0; i < 100; i++) {
            new Thread(atom).start();
        }
    }
}
class MyAtomThread implements Runnable {
    private volatile int count = 0; //送冰淇淋的數量
    private Object lock = new Object();
​
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //1,從共享數據中讀取數據到本線程棧中.
            //2,修改本線程棧中變量副本的值
            //3,會把本線程棧中變量副本的值賦值給共享數據.
            synchronized (lock) {
                count++;
                System.out.println("已經送了" + count + "個冰淇淋");
            }
        }
    }
}

 

 

2.6 原子性_AtomicInteger

概述:java從JDK1.5開始提供了java.util.concurrent.atomic包(簡稱Atomic包),這個包中的原子操作類提供了一種用法簡單,性能高效,線程安全地更新一個變量的方式。因為變

量的類型有很多種,所以在Atomic包里一共提供了13個類,屬於4種類型的原子更新方式,分別是原子更新基本類型、原子更新數組、原子更新引用和原子更新屬性(字段)。本次我們只講解

使用原子的方式更新基本類型,使用原子的方式更新基本類型Atomic包提供了以下3個類:

AtomicBoolean: 原子更新布爾類型

AtomicInteger: 原子更新整型

AtomicLong: 原子更新長整型

以上3個類提供的方法幾乎一模一樣,所以本節僅以AtomicInteger為例進行講解,AtomicInteger的常用方法如下:

public AtomicInteger():                 初始化一個默認值為0的原子型Integer
public AtomicInteger(int initialValue):  初始化一個指定值的原子型Integer
​
int get():                              獲取值
int getAndIncrement():                   以原子方式將當前值加1,注意,這里返回的是自增前的值。
int incrementAndGet():                   以原子方式將當前值加1,注意,這里返回的是自增后的值。
int addAndGet(int data):                 以原子方式將輸入的數值與實例中的值(AtomicInteger里的value)相加,並返回結果。
int getAndSet(int value):                以原子方式設置為newValue的值,並返回舊值。

 

代碼實現 :

package com.itheima.threadatom3;
​
import java.util.concurrent.atomic.AtomicInteger;
​
public class MyAtomIntergerDemo1 {
//    public AtomicInteger():                  初始化一個默認值為0的原子型Integer
//    public AtomicInteger(int initialValue): 初始化一個指定值的原子型Integer
    public static void main(String[] args) {
        AtomicInteger ac = new AtomicInteger();
        System.out.println(ac);
​
        AtomicInteger ac2 = new AtomicInteger(10);
        System.out.println(ac2);
    }
​
}
package com.itheima.threadatom3;
​
import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicInteger;
​
public class MyAtomIntergerDemo2 {
//    int get():                獲取值
//    int getAndIncrement():     以原子方式將當前值加1,注意,這里返回的是自增前的值。
//    int incrementAndGet():     以原子方式將當前值加1,注意,這里返回的是自增后的值。
//    int addAndGet(int data):   以原子方式將參數與對象中的值相加,並返回結果。
//    int getAndSet(int value):  以原子方式設置為newValue的值,並返回舊值。
    public static void main(String[] args) {
//        AtomicInteger ac1 = new AtomicInteger(10);
//        System.out.println(ac1.get());
//        AtomicInteger ac2 = new AtomicInteger(10);
//        int andIncrement = ac2.getAndIncrement();
//        System.out.println(andIncrement);
//        System.out.println(ac2.get());
//        AtomicInteger ac3 = new AtomicInteger(10);
//        int i = ac3.incrementAndGet();
//        System.out.println(i);//自增后的值
//        System.out.println(ac3.get());
//        AtomicInteger ac4 = new AtomicInteger(10);
//        int i = ac4.addAndGet(20);
//        System.out.println(i);
//        System.out.println(ac4.get());
​
        AtomicInteger ac5 = new AtomicInteger(100);
        int andSet = ac5.getAndSet(20);
        System.out.println(andSet);
        System.out.println(ac5.get());
    }
}

 

 

2.7 AtomicInteger-內存解析

AtomicInteger原理 : 自旋鎖 + CAS 算法

CAS算法:

有3個操作數(內存值V, 舊的預期值A,要修改的值B)

當舊的預期值A == 內存值 此時修改成功,將V改為B

當舊的預期值A!=內存值 此時修改失敗,不做任何操作

並重新獲取現在的最新值(這個重新獲取的動作就是自旋)

2.8 AtomicInteger-源碼解析

代碼實現 :

package com.itheima.threadatom4;
​
public class AtomDemo {
    public static void main(String[] args) {
        MyAtomThread atom = new MyAtomThread();
​
        for (int i = 0; i < 100; i++) {
            new Thread(atom).start();
        }
    }
}
package com.itheima.threadatom4;
​
import java.util.concurrent.atomic.AtomicInteger;
​
public class MyAtomThread implements Runnable {
    //private volatile int count = 0; //送冰淇淋的數量
    //private Object lock = new Object();
    AtomicInteger ac = new AtomicInteger(0);
​
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //1,從共享數據中讀取數據到本線程棧中.
            //2,修改本線程棧中變量副本的值
            //3,會把本線程棧中變量副本的值賦值給共享數據.
            //synchronized (lock) {
//                count++;
//                ac++;
            int count = ac.incrementAndGet();
            System.out.println("已經送了" + count + "個冰淇淋");
           // }
        }
    }
}

 

源碼解析 :


//先自增,然后獲取自增后的結果
public final int incrementAndGet() {
        //+ 1 自增后的結果
        //this 就表示當前的atomicInteger(值)
        //1    自增一次
        return U.getAndAddInt(this, VALUE, 1) + 1;
}
​
public final int getAndAddInt(Object o, long offset, int delta) {
        //v 舊值
        int v;
        //自旋的過程
        do {
            //不斷的獲取舊值
            v = getIntVolatile(o, offset);
            //如果這個方法的返回值為false,那么繼續自旋
            //如果這個方法的返回值為true,那么自旋結束
            //o 表示的就是內存值
            //v 舊值
            //v + delta 修改后的值
        } while (!weakCompareAndSetInt(o, offset, v, v + delta));
            //作用:比較內存中的值,舊值是否相等,如果相等就把修改后的值寫到內存中,返回true。表示修改成功。
            //                                 如果不相等,無法把修改后的值寫到內存中,返回false。表示修改失敗。
            //如果修改失敗,那么繼續自旋。
        return v;
}
 
        

2.9 悲觀鎖和樂觀鎖

synchronized和CAS的區別 :

相同點:在多線程情況下,都可以保證共享數據的安全性。

不同點:synchronized總是從最壞的角度出發,認為每次獲取數據的時候,別人都有可能修改。所以在每 次操作共享數據之前,都會上鎖。(悲觀鎖)

cas是從樂觀的角度出發,假設每次獲取數據別人都不會修改,所以不會上鎖。只不過在修改共享數據的時候,會檢查一下,別人有沒有修改過這個數據。

如果別人修改過,那么我再次獲取現在最新的值。

如果別人沒有修改過,那么我現在直接修改共享數據的值.(樂觀鎖)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM