深入淺出java原子操作

原文地址:深入淺出java原子操作作者:opaljc

轉自http://www.blogjava.net/xylz/archive/2010/07/04/325206.html

part1 從AtomicInteger開始

 

從相對簡單的Atomic入手(java.util.concurrent是基於Queue的併發包,而Queue,很多情況下使用到了Atomic操作,因此首先從這裏開始)。很多情況下我們只是需要一個簡單的、高效的、線程安全的遞增遞減方案。注意,這裏有三個條件:簡單,意味着程序員儘可能少的操作底層或者實現起來要比較容易;高效意味着耗用資源要少,程序處理速度要快;線程安全也非常重要,這個在多線程下能保證數據的正確性。這三個條件看起來比較簡單,但是實現起來卻難以令人滿意。

通常情況下,在Java裏面,++i或者--i不是線程安全的,這裏面有三個獨立的操作:或者變量當前值,爲該值+1/-1,然後寫回新的值。在沒有額外資源可以利用的情況下,只能使用加鎖才能保證讀-改-寫這三個操作時“原子性”的。

Doug Lea在未將backport-util-concurrent 合併到JSR 166 裏面來之前,是採用純Java實現的,於是不可避免的採用了synchronized關鍵字。

public final synchronized void set(int newValue);

public final synchronized int getAndSet(int newValue);

public final synchronized int incrementAndGet();

同時在變量上使用了volatile (後面會具體來講volatile到底是個什麼東東)來保證get()的時候不用加鎖。儘管synchronized的代價還是很高的,但是在沒有JNI的手段下純Java語言還是不能實現此操作的。

JSR 166提上日程後,backport-util-concurrent就合併到JDK 5.0裏面了,在這裏面重複使用了現代CPU的特性來降低鎖的消耗。後本章的最後小結中會談到這些原理和特性。在此之前先看看API的使用。

一切從java.util.concurrent.atomic.AtomicInteger開始。

int addAndGet(int delta)
          以原子方式將給定值與當前值相加。 實際上就是等於線程安全版本的i =i+delta操作。

boolean compareAndSet(int expect, int update)
          如果當前值 == 預期值,則以原子方式將該值設置爲給定的更新值。 如果成功就返回true,否則返回false,並且不修改原值。

int decrementAndGet()
          以原子方式將當前值減 1。 相當於線程安全版本的--i操作。

int get()
          獲取當前值。

int getAndAdd(int delta)
          以原子方式將給定值與當前值相加。 相當於線程安全版本的t=i;i+=delta;return t;操作。

int getAndDecrement()
          以原子方式將當前值減 1。 相當於線程安全版本的i--操作。

int getAndIncrement()
          以原子方式將當前值加 1。 相當於線程安全版本的i++操作。

int getAndSet(int newValue)
          以原子方式設置爲給定值,並返回舊值。 相當於線程安全版本的t=i;i=newValue;return t;操作。

int incrementAndGet()
          以原子方式將當前值加 1。 相當於線程安全版本的++i操作。 

void lazySet(int newValue)
          最後設置爲給定值。延時設置變量值,這個等價於set()方法,但是由於字段是volatile類型的,因此次字段的修改會比普通字段(非volatile字段)有稍微的性能延時(儘管可以忽略),所以如果不是想立即讀取設置的新值,允許在“後臺”修改值,那麼此方法就很有用。如果還是難以理解,這裏就類似於啓動一個後臺線程如執行修改新值的任務,原線程就不等待修改結果立即返回(這種解釋其實是不正確的,但是可以這麼理解)。

void set(int newValue)
          設置爲給定值。 直接修改原始值,也就是i=newValue操作。

boolean weakCompareAndSet(int expect, int update)
          如果當前值 == 預期值,則以原子方式將該設置爲給定的更新值。JSR規範中說:以原子方式讀取和有條件地寫入變量但   創建任何 happen-before 排序,因此不提供與除  weakCompareAndSet 目標外任何變量以前或後續讀取或寫入操作有關的任何保證。大意就是說調用weakCompareAndSet時並不能保證不存在happen-before的發生(也就是可能存在指令重排序導致此操作失敗)。但是從Java源碼來看,其實此方法並沒有實現JSR規範的要求,最後效果和compareAndSet是等效的,都調用了 unsafe.compareAndSwapInt()完成操作。


下面的代碼是一個測試樣例,爲了省事就寫在一個方法裏面來了。
 package  xylz.study.concurrency.atomic;
  
  
import  java.util.concurrent.atomic.AtomicInteger;
  
  
import  org.junit.Test;
  
  
import   static  org.junit.Assert. * ;
  
  
public   class  AtomicIntegerTest  {
  
      @Test
      
 public   void  testAll()  throws  InterruptedException {
          
 final  AtomicInteger value  =   new  AtomicInteger( 10 );
          assertEquals(value.compareAndSet(
 1  2 ),  false );
          assertEquals(value.get(), 
 10 );
          assertTrue(value.compareAndSet(
 10  3 ));
          assertEquals(value.get(), 
 3 );
          value.set(
 0 );
          
 //
  
        assertEquals(value.incrementAndGet(),  1 );
          assertEquals(value.getAndAdd(
 2 ), 1 );
          assertEquals(value.getAndSet(
 5 ), 3 );
          assertEquals(value.get(),
 5 );
          
 //
  
         final   int  threadSize  =   10 ;
          Thread[] ts 
 =   new  Thread[threadSize];
          
 for  ( int  i  =   0 ; i  <  threadSize; i ++  {
              ts[i] 
 =   new  Thread()  {
                  
 public   void  run()  {
                      value.incrementAndGet();
           
發佈了4 篇原創文章 · 獲贊 3 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章