首先volatile是java中關鍵字用於修飾變量,AtomicReference是並發包java.util.concurrent.atomic下的類。
首先volatile作用,當一個變量被定義為volatile之后,看做“程度較輕的 synchronized”,具備兩個特性:
1.保證此變量對所有線程的可見性(當一條線程修改這個變量值時,新值其他線程立即得知)
2.禁止指令重新排序
注意volatile修飾變量不能保證在並發條件下是線程安全的,因為java里面的運算並非原子操作。
java.util.concurrent.atomic工具包,支持在單個變量上解除鎖的線程安全編程。其基本的特性就是在多線程環境下,當有多個線程同時執行這些類的實例包含的方法時,具有排他性,即當某個線程進入方法,執行其中的指令時,不會被其他線程打斷,而別的線程就像自旋鎖一樣,一直等到該方法執行完成,才由JVM從等待隊列中選擇一個另一個線程進入,這只是一種邏輯上的理解。
volatile是不能保證原子性的, 寫了一點junit. 這里使用了包裝類Integer, 來驗證 對引用操作 的原子性. 可以看到使用了AtomicReference可以保證結果是正確的.
1 private static volatile Integer num1 = 0; 2 private static AtomicReference<Integer> ar=new AtomicReference<Integer>(num1); 3 4 @Test 5 public void dfasd111() throws InterruptedException{ 6 for (int i = 0; i < 1000; i++) { 7 new Thread(new Runnable(){ 8 @Override 9 public void run() { 10 for (int i = 0; i < 10000; i++) 11 while(true){ 12 Integer temp=ar.get(); 13 if(ar.compareAndSet(temp, temp+1))break; 14 } 15 } 16 }).start(); 17 } 18 Thread.sleep(10000); 19 System.out.println(ar.get()); //10000000 20 } 21 22 @Test 23 public void dfasd1112() throws InterruptedException{ 24 for (int i = 0; i < 1000; i++) { 25 new Thread(new Runnable(){ 26 @Override 27 public void run() { 28 for (int i = 0; i < 10000; i++) { 29 num1=num1++; 30 } 31 } 32 }).start(); 33 } 34 Thread.sleep(10000); 35 System.out.println(num1); //something like 238981 36 }
如果想讓運算具有原子性, 請使用:
AtomicInteger
AtomicLong
類似i++這樣的"讀-改-寫"復合操作(在一個操作序列中, 后一個操作依賴前一次操作的結果), 在多線程並發處理的時候會出現問題, 因為可能一個線程修改了變量, 而另一個線程沒有察覺到這樣變化, 當使用原子變量之后, 則將一系列的復合操作合並為一個原子操作,從而避免這種問題, i++=>i.incrementAndGet()
原子變量只能保證對一個變量的操作是原子的, 如果有多個原子變量之間存在依賴的復合操作, 也不可能是安全的, 另外一種情況是要將更多的復合操作作為一個原子操作, 則需要使用synchronized將要作為原子操作的語句包圍起來. 因為涉及到可變的共享變量(類實例成員變量)才會涉及到同步, 否則不必使用synchronized