執行緒安全性---面試題--i++的執行緒安全性問題
這個類真的非常實用,更重要的是 它確實非常簡單:
附上自己的程式碼,可以自己試試:
AtomicInteger,一個提供原子操作的Integer的類。在Java語言中,++i和i++操作並不是執行緒安全的,在使用的時候,不可避免的會用到synchronized關鍵字。而AtomicInteger則通過一種執行緒安全的加減操作介面。
程式碼:
package test;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 來看AtomicInteger提供的介面。
//獲取當前的值
public final int get()
//取當前的值,並設定新的值
public final int getAndSet(int newValue)
//獲取當前的值,並自增
public final int getAndIncrement()
//獲取當前的值,並自減
public final int getAndDecrement()
//獲取當前的值,並加上預期的值
public final int getAndAdd(int delta)
* @author YangBaoBao
*
*/
public class AtomicIntegerDemo {
public static void main(String[] args) {
AtomicInteger ai=new AtomicInteger(0);
int i1=ai.get();
v(i1);
int i2=ai.getAndSet(5);
v(i2);
int i3=ai.get();
v(i3);
int i4=ai.getAndIncrement();
v(i4);
v(ai.get());
}
static void v(int i)
{
System.out.println("i : "+i);
}
}
那麼為什麼不使用記數器自加呢,例如count++這樣的,因為這種計數是執行緒不安全的,高併發訪問時統計會有誤,而AtomicInteger為什麼能夠達到多而不亂,處理高併發應付自如呢,我們才看看AtomicInteger的原始碼:
Java程式碼- private volatile int value;
大家可以看到有這個變數,value就是你設定的自加起始值。注意看它的訪問控制符,是volatile,這個就是保證AtomicInteger執行緒安全的根源,熟悉併發的同學一定知道在java中處理併發主要有兩種方式:
1,synchronized關鍵字,這個大家應當都各種面試和筆試中經常遇到。
2,volatile修飾符的使用,相信這個修飾符大家平時在專案中使用的也不是很多。
這裡重點說一下volatile:
Volatile修飾的成員變數在每次被執行緒訪問時,都強迫從共享記憶體重新讀取該成員的值,而且,當成員變數值發生變化時,強迫將變化的值重新寫入共享記憶體,這樣兩個不同的執行緒在訪問同一個共享變數的值時,始終看到的是同一個值。
java語言規範指出:為了獲取最佳的執行速度,允許執行緒保留共享變數的副本,當這個執行緒進入或者離開同步程式碼塊時,才與共享成員變數進行比對,如果有變化再更新共享成員變數。這樣當多個執行緒同時訪問一個共享變數時,可能會存在值不同步的現象。
而volatile這個值的作用就是告訴VM:對於這個成員變數不能儲存它的副本,要直接與共享成員變數互動。
建議:當多個執行緒同時訪問一個共享變數時,可以使用volatile,而當訪問的變數已在synchronized程式碼塊中時,不必使用。
缺點:使用volatile將使得VM優化失去作用,導致效率較低,所以要在必要的時候使用。