分析:
如果是全局變量,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();
}
}
參考:
