i++為什么是線程不安全的


分析:

如果是全局變量,i++是不安全的,因為java在操作i++的時候,是分步驟做的,可以理解為:
temp = i;
temp2 = i+1;
i=temp2;
如果線程1在執行第一條代碼的時候,線程2訪問i變量,這個時候,i的值還沒有變化,還是原來的值,所以是不安全的。

從更底層的角度講,主要是因為i++這個操作不是原子性的,這個會編譯成i= i +1; 所以會出現多線程訪問沖突問題。volatile雖然可以保證多線程對修改可見,但代碼中用到了
i++, 主要是i++不是原子性操作,這個會編譯成i = i +1,其實是做了3個步驟,一個是讀取,修改,寫入 。所以會出現多線程訪問沖突問題

解決方案:

在Java語言中,++i和i++操作並不是線程安全的,在使用的時候,不可避免的會用到synchronized關鍵字。而java.util.concurrent.AtomicInteger是一個提供原子操作的Integer類,其提供了線程安全且高效的原子操作,是線程安全的。

AtomicInteger類的底層實現原理是利用處理器的CAS操作(Compare And Swap,比較與交換,一種有名的無鎖算法)來檢測棧中的值是否被其他線程改變,如果被改變則CAS操作失敗。這種實現方法在CPU指令級別實現了原子操作,因此,其比使用synchronized來實現同步效率更高。

CAS操作過程都包含三個運算符:內存地址V、期望值E、新值N。當操作的時候,如果地址V上存放的值等於期望值E,則將地址V上的值賦為新值N,否則,不做任何操作,但是要返回原值是多少。這就保證比較和設置這兩個動作是原子操作。系統主要利用JNI(Java Native Interface,Java本地接口)來保證這個原子操作,它利用CPU硬件支持來完成,使用硬件提供swap和test_and_set指令,但CPU下同一指令的多個指令周期不可中斷,SMP(Symmetric Multi-Processing)中通過鎖總線支持這兩個指令的原子性

示例代碼:

  

package com.tree.blog.pool.cas;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicTest {

     volatile  int initialValue=1;
     //Atomic包下的是基於CAS實現的,是線程安全的,不需要加鎖
     AtomicInteger initialAtomicValue=new AtomicInteger(1);

    /**
     * i++ 是線程不安去的
     *   這個語句在執行的時候實際分為了三部
     *   1、在棧中給一個 tp=i
     *   2、執行i+1,    tp2=i+1
     *   3、賦值 給i完成,i=tp2
     *   如果線程1在執行第一條代碼的時候,線程2訪問i變量,這個時候,i的值還沒有變化,還是原來的值,所以是不安全的
     * @param args
     */
    public static void main(String[] args)  {
        AtomicTest test = new AtomicTest();
        ExecutorService executorService = Executors.newFixedThreadPool(3000);
        //使用Callable+future可以接受返回值其它的不行
        Runnable run = test::increment;
        Runnable runLock = test::incrementWithLock;
        Runnable atomicRun = test::atomicIncrement;
        //線程不安全,會出現多個線程得到相同的值
        for (int i = 0; i <1000 ; i++) {
            executorService.submit(run);
        }
        //同步鎖,不會出粗,並發性能低
        for (int i = 0; i <1000 ; i++) {
           executorService.submit(runLock);
        }
        //使用基於CAS實現的原子操作,不錯出現重復
        for (int i = 0; i <1000 ; i++) {
            executorService.submit(atomicRun);
        }
        executorService.shutdown();
    }

    /**
     * 同步鎖 執行自增
     * @return
     */
   public synchronized  int incrementWithLock(){
       System.out.println("線程名稱:"+Thread.currentThread().getName()+"-當前值:"+initialValue);
        return initialValue++;
   }
    /**
     * 執行自增
     * @return
     */
    public  int increment(){
        System.out.println("線程名稱:"+Thread.currentThread().getName()+"-當前值:"+initialValue);
        return initialValue++;
    }

    /**
     * 同步鎖 執行自增
     * @return
     */
    public synchronized int atomicIncrement(){
        System.out.println("線程名稱:"+Thread.currentThread().getName()+"-當前值:"+initialAtomicValue.get());
        return initialAtomicValue.getAndIncrement();
    }
}

  參考:

  https://www.jianshu.com/p/ae25eb3cfb5d


免責聲明!

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



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