多線程atomicInteger


並發編程的3個重要概念

1.原子性:

一個操作或者多個操作,要么全部成功,要么全部失敗 

1.java中保證了基本數據類型的讀取和賦值,保證了原子性,這些操作不可終端

a=10  原子性
b=a    不滿足 1.read a; 2.assign b;
c++    不滿足  1.read c; 2 add 3.assige to c 
c=c+1  不滿足 1.read c 2.add; 3. assign to c;

 

2.可見性:

volatile 可以保證可見性.主要是把變量放在主存里

多個線程訪問這個變量,一個線程修改之后必須保證另一個線程可以看見。

每一個線程都自己的緩存,有的變量在主存區,我們要保證變量的可見性

4.順序性:

java中hapens-before 原則保證了有序性

4.1 代碼順序(程序順序規則): 單個線程中的每個操作    編寫在前面的反生在編寫在后面的

4.2 lock原則(監視器鎖規則) : unlock 必須發生在lock之后

4.3 volatile變量規則: 對該變量的寫操作必須發生在讀操作之前.就是一個變量 寫變量發生在讀變量之前

4.4 傳遞規則: a->b->c  那么a肯定在c之前

4.5 start規則:   先start 線程在啟動(個人認為廢話)

4.6 中斷規則: 對線程的中斷 先發生在 捕獲interpetor異常之前(個人認為廢話)

4.7 對象的銷毀規則: 一個對象的初始化.必須發生在finalize 之前(個人認為廢話)

4.8 線程的終結規則: 所有操作都發生在線程死亡之前 (個人認為廢話)

比如定義一個變量

int i =0;

boolean flag =false;

int i =1;

boolean flag =true;

java中有重排序的過程 因為這幾條代碼沒有關聯性可能會變成

int i =0;

boolean flag =false;

boolean flag =true;

int i =1;

 

重排序只保證最終一致性,重排序也是有依賴關系的,重排序對沒有依賴關系的可以重排序 ,重排序不會影響單線程,但是會影響多線程

舉個例子

--------thread-1 ==========

obj =createObj();1
flag = true; 2
如果這里發生指令重排將  1 -2 順序顛倒下面下面的代碼就會報出空指針異常

-----thread-2------
while(!flag){
}
useObject()

 

atomic 具有原子性

 主要有倆點 1.他的變量是volatile

       2.unsafe方法調用

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

 

什么時候需要使用atomic類對象

當多線程操作一個變量的時候。如果使用傳統的變量會出錯,很多同學為了解決這個問題,加了votatile修飾。也並不能夠解決問題。

補充下votalite 的知識

volatile具有可見性、有序性,不具備原子性。

當value+=1時候我們需要做幾個操作

1.從主存區獲取變量到寫存區

2.value+1

3.value = 新的value

4.講變量寫入主存區  

這4步都是原子性的。但是在多線程環境中。可能你讀取到的變量是別人修改過的變量造成線程不安全情況  

  private static Set<Integer> set = Collections.synchronizedSet(new HashSet<>());

    public static void main(String[] args) throws InterruptedException {

       Thread t1= new Thread(()->{
            int x=0;
            while (x<500){
                set.add(value);
                int temp =value;
                System.out.println(Thread.currentThread().getName()+"   "+temp);
                value +=1;
                x++;

            }
        });
        Thread t2= new Thread(()->{
            int x=0;
            while (x<500){
                set.add(value);
                int temp =value;
                System.out.println(Thread.currentThread().getName()+"   "+temp);
                value +=1;
                x++;

            }
        });
        Thread t3= new Thread(()->{
            int x=0;
            while (x<500){
                set.add(value);
                int temp =value;
                System.out.println(Thread.currentThread().getName()+"   "+temp);
                value +=1;
                x++;

            }
        });

        t1.start();
        t2.start();
        t3.start();
       t1.join();


        t2.join();

        t3.join();
        System.out.println(set.size());
」
View Code

當我們使用aomic類時。可以保證原子性

     AtomicInteger atoValue = new AtomicInteger();
        Thread t1= new Thread(()->{
            int x=0;
            while (x<500){
                int i = atoValue.getAndIncrement();
                set.add(i);
                System.out.println(Thread.currentThread().getName()+"   "+i);
                atoValue.getAndIncrement();
                x++;

            }
        });
        Thread t3= new Thread(()->{
            int x=0;
            while (x<500){
                int i =atoValue.getAndIncrement();
                set.add(i);
                System.out.println(Thread.currentThread().getName()+"   "+i);
                atoValue.getAndIncrement();
                x++;

            }
        });
        Thread t2= new Thread(()->{
            int x=0;
            while (x<500){
                int i = atoValue.getAndIncrement();
                set.add(i);
                System.out.println(Thread.currentThread().getName()+"   "+i);

                x++;

            }
        });
        t1.start();
        t2.start();
        t3.start();
        t1.join();
        t2.join();
        t3.join();
        System.out.println(set.size());

 下載jdk源碼

http://download.java.net/openjdk/jdk8/promoted/b132/openjdk-8-src-b132-03_mar_2014.zip

源碼查看

/openjdk/jdk/src/share/classes/sun/misc/Unsafe.java 

atomic保證原子性操作是因為有unsafe.java 

很多人都說unsafe.java 是java留的一后門可以直接操作cpu級別的內存

因為UnSafe提供了硬件級別的原子操作,提高了Java對底層操作的能力。Unsafe類使Java語言擁有了像C語言的指針一樣的操作內存空間的能力

一篇比較好的博客

https://www.cnblogs.com/pkufork/p/java_unsafe.html

如何保證原子性

/**
     * Atomically adds the given value to the current value of a field
     * or array element within the given object <code>o</code>
     * at the given <code>offset</code>.
     *
     * @param o object/array to update the field/element in
     * @param offset field/element offset
     * @param delta the value to add
     * @return the previous value
     * @since 1.8
     */

public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
  1、需要修改的對象
2、更改屬性的內存偏移量

4、要添加的 1
public final int getAndAddInt(Object o, long offset, int delta) { 
int v;

do {
  v
= getIntVolatile(o, offset);
}

while (!compareAndSwapInt(o, offset, v, v + delta)); return v;
}
其實主要原子性是通過compareAndSwapInt對比算法.傳入你開始的值.和預期的值 如果不相同就一直執行下去直到相同為止,達到很多人說的無鎖概念,其實我更傾向於自旋鎖
假設多線程調用getAndIncrement
假設初始值是1
compareAndWapInt(1,1+1)
compareAndWapInt(1,1+1)

1 2 比較false

進行下一次 2==2

2

1 2比較 false

2 3 比較false

3

這樣就實現了free lock

獲取unsafe.實例利用java反射

   Field f = Unsafe.class.getDeclaredField("theUnsafe");
         f.setAccessible(true);
         Unsafe unsafe = (Unsafe) f.get(null);

自己測試類

  public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {

         Field f1 = Unsafe.class.getDeclaredField("theUnsafe");
         f1.setAccessible(true);
         Unsafe unsafe = (Unsafe) f1.get(null);
        Class clazz = Target.class;
        Field[] fields = clazz.getDeclaredFields();
        for (Field f : fields) {
            // 獲取屬性偏移量,可以通過這個偏移量給屬性設置
            System.out.println(f.getName() + ":" + unsafe.objectFieldOffset(f));
        }
        Target target = new Target();
        Field intFiled  =  clazz.getDeclaredField("intParam")  ;
//        int a=(Integer)intFiled.get(target ) ;
//        System.out.println("原始值是:"+a);
        //intParam的字段偏移是12 原始值是3 我們要改為10
        System.out.println(unsafe.compareAndSwapInt(target, 12, 3, 10));
        int b=(Integer)intFiled.get(target) ;
        System.out.println("改變之后的值是:"+b);
//
//        //這個時候已經改為10了,所以會返回false
        System.out.println(unsafe.compareAndSwapInt(target, 12, 3, 10));
//
//        System.out.println(unsafe.compareAndSwapObject(target, 24, null, "5"));
    }


}
class Target {
    int intParam=3;
    long longParam;
    String strParam;
    String strParam2;
}
 
        

 


免責聲明!

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



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