1. 程式人生 > >Java併發學習(九)-AtomicIntegerFieldUpdater欄位原子更新類

Java併發學習(九)-AtomicIntegerFieldUpdater欄位原子更新類

前面講的兩個AtomicInteger和AtomicIntegerArray,這兩個都是在最初設計編碼時候就已經考慮到了需要保證原子性。但是往往有很多情況就是,由於需求的更改,原子性需要在後面加入,類似於我不要求你這整個類操作具有原子性,我只要求你裡面一個欄位操作具有原子性。沒錯,concurrent.atomic包下AtomicIntegerFieldUpdater就是這個作用的。

AtomicXXXFieldUpdater主要包括以下幾個:AtomicIntegerFieldUpdaterAtomicLongFieldUpdaterAtomicReferenceFieldUpdater

What is AtomicIntegerFieldUpdater

相信前言部分講的已經很清晰易懂了,AtomicIntegerFieldUpdater就是用來更新某一個例項物件裡面的int屬性的。
但是注意,在用法上有規則:

  • 欄位必須是volatile型別的,線上程之間共享變數時保證立即可見
  • 欄位的描述型別(修飾符public/protected/default/private)是與呼叫者與操作物件欄位的關係一致。也就是說呼叫者能夠直接操作物件欄位,那麼就可以反射進行原子操作。
  • 對於父類的欄位,子類是不能直接操作的,儘管子類可以訪問父類的欄位。
  • 只能是例項變數,不能是類變數,也就是說不能加static關鍵字。
  • 只能是可修改變數,不能使final變數,因為final的語義就是不可修改。
  • 對於AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long型別的欄位,不能修改其包裝型別(Integer/Long)。如果要修改包裝型別就需要使用AtomicReferenceFieldUpdater。

具體規則可以通過以下測試例子來分析:

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class AtomicIntegerFieldUpdaterAnalyzeTest {

    public
static void main(String[] args) { AtomicIntegerFieldUpdaterAnalyzeTest test = new AtomicIntegerFieldUpdaterAnalyzeTest(); test.testValue(); } public AtomicIntegerFieldUpdater<DataDemo> updater(String name) { return AtomicIntegerFieldUpdater.newUpdater(DataDemo.class, name); } public void testValue() { DataDemo data = new DataDemo(); // //訪問父類的public 變數,報錯:java.lang.NoSuchFieldException // System.out.println("fatherVar = "+updater("fatherVar").getAndIncrement(data)); // // //訪問普通 變數,報錯:java.lang.IllegalArgumentException: Must be volatile type // System.out.println("intVar = "+updater("intVar").getAndIncrement(data)); // //訪問public volatile int 變數,成功 // System.out.println("publicVar = "+updater("publicVar").getAndIncrement(data)); // // //訪問protected volatile int 變數,成功 // System.out.println("protectedVar = "+updater("protectedVar").getAndIncrement(data)); // // //訪問其他類private volatile int變數,失敗:java.lang.IllegalAccessException // System.out.println("privateVar = "+updater("privateVar").getAndIncrement(data)); // // //訪問,static volatile int,失敗,只能訪問例項物件:java.lang.IllegalArgumentException // System.out.println("staticVar = "+updater("staticVar").getAndIncrement(data)); // // //訪問integer變數,失敗, Must be integer type // System.out.println("integerVar = "+updater("integerVar").getAndIncrement(data)); // // //訪問long 變數,失敗, Must be integer type // System.out.println("longVar = "+updater("longVar").getAndIncrement(data)); //自己在自己函式裡面可以訪問自己的private變數,所以如果可見,那麼可以進行原子性欄位更新 data.testPrivate(); } } class Father{ public volatile int fatherVar = 4; } class DataDemo extends Father { public int intVar = 4; public volatile int publicVar = 3; protected volatile int protectedVar = 4; private volatile int privateVar = 5; public volatile static int staticVar = 10; //The field finalVar can be either final or volatile, not both //public final volatile int finalVar = 11; public volatile Integer integerVar = 19; public volatile Long longVar = 18L; public void testPrivate(){ DataDemo data = new DataDemo(); System.out.println(AtomicIntegerFieldUpdater.newUpdater(DataDemo.class, "privateVar").getAndIncrement(data)); }

實現

首先看定義:

/**
 * 允許一個已經定義的類裡面的某一個volatile int型變數的原子更新。
 * 注意只能夠原子更新裡面的某一個int型的變數。
 * 思路是通過反射獲取變數,為一個updater,然後進行更新。
 */
public abstract class AtomicIntegerFieldUpdater<T> 

AtomicIntegerFieldUpdater是一個抽象類,但是它內部有一個private final型別的預設子類,所以在呼叫newUpdater的時候,會用模式子類來實現:

/**
     * 建立一個updater,
     * tclass:包含類的名稱
     * fieldName:欄位名字
     */
    @CallerSensitive
    public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
                                                              String fieldName) {
        return new AtomicIntegerFieldUpdaterImpl<U>
            (tclass, fieldName, Reflection.getCallerClass());
    }

而除了這些之外,子類中還有判斷物件訪問許可權,以及判斷是否為父類,是否同一個包等方法:

//判斷second是否為first的父類
private static boolean isAncestor(ClassLoader first, ClassLoader second) ;
//判斷class1和class2是否在同一個包下
private static boolean isSamePackage(Class<?> class1, Class<?> class2)
//獲得包名
private static String getPackageName(Class<?> cls)
//判斷object是否為當前class的一個子類
private final void accessCheck(T obj)

另外就是一些CAS方法,實際上都是呼叫Unsafe.java中的native方法:

 public final boolean compareAndSet(T obj, int expect, int update) {
            accessCheck(obj);
            return U.compareAndSwapInt(obj, offset, expect, update);
        }

        public final boolean weakCompareAndSet(T obj, int expect, int update) {
            accessCheck(obj);
            return U.compareAndSwapInt(obj, offset, expect, update);
        }

        public final void set(T obj, int newValue) {
            accessCheck(obj);
            U.putIntVolatile(obj, offset, newValue);
        }

        public final void lazySet(T obj, int newValue) {
            accessCheck(obj);
            U.putOrderedInt(obj, offset, newValue);
        }

        public final int get(T obj) {
            accessCheck(obj);
            return U.getIntVolatile(obj, offset);
        }

        public final int getAndSet(T obj, int newValue) {
            accessCheck(obj);
            return U.getAndSetInt(obj, offset, newValue);
        }

AtomicLongFieldUpdater和AtomicReferenceFieldUpdater

在AtomicLongFieldUpdater類中,由於有些32位系統一次性無法對64位的long進行原子運算,所以為了保證安全,在這些不能一次性進行原子運算的需要區分考慮,利用加synchronized鎖來實現:

  @CallerSensitive
    public static <U> AtomicLongFieldUpdater<U> newUpdater(Class<U> tclass,
                                                           String fieldName) {
        Class<?> caller = Reflection.getCallerClass();
        if (AtomicLong.VM_SUPPORTS_LONG_CAS)
        //直接cas實現
            return new CASUpdater<U>(tclass, fieldName, caller);
        else
        //帶synchronized鎖實現
            return new LockedUpdater<U>(tclass, fieldName, caller);
    }

什麼意思呢?下面給出幾個具體方法:

直接CAS實現:

        //直接CAS實現
        public final boolean compareAndSet(T obj, long expect, long update) {
            accessCheck(obj);
            return U.compareAndSwapLong(obj, offset, expect, update);
        }

加鎖的CAS實現:

        //加鎖的CAS實現
        public final boolean compareAndSet(T obj, long expect, long update) {
            accessCheck(obj);
            synchronized (this) {
                long v = U.getLong(obj, offset);
                if (v != expect)
                    return false;
                U.putLong(obj, offset, update);
                return true;
            }
        }

在其他方面, AtomicLongFieldUpdater和AtomicReferenceFieldUpdater實現思想基本一致。

相關推薦

Java併發學習()-AtomicIntegerFieldUpdater原子更新

前面講的兩個AtomicInteger和AtomicIntegerArray,這兩個都是在最初設計編碼時候就已經考慮到了需要保證原子性。但是往往有很多情況就是,由於需求的更改,原子性需要在後面加入,類似於我不要求你這整個類操作具有原子性,我只要求你裡面一個欄位操

Java併發 行級鎖/鎖/表級鎖 樂觀鎖/悲觀鎖 共享鎖/排他鎖 死鎖[轉]

前言 鎖是防止在兩個事務操作同一個資料來源(表或行)時互動破壞資料的一種機制。 資料庫採用封鎖技術保證併發操作的可序列性。 以Oracle為例: Oracle的鎖分為兩大類:資料鎖(也稱DML鎖)和字典鎖。 字典鎖是Oracle DBMS內部用於對字典表的封鎖。 字典鎖包括語

Java併發程式設計的藝術之七----原子更新基本型別

1.原子更新基本型別 ·AtomicBoolean:原子更新布林型別。 ·AtomicInteger:原子更新整型。 ·AtomicLong:原子更新長整型 ·int addAndGet(int delta):以原子方式將輸入的數值與例項中的值(AtomicInteger裡的valu

Java併發學習筆記()-原子AtomicInteger

AtomicInteger能夠保證對一個整型的操作是原子性。像i++這個操作不是原子操作,存在競態條件,所以需要加鎖,但是加鎖的效能不高,如果僅僅為了對一個整數加1。我們來看下他的實現。 private volatile int value; AtomicInte

Android-NDK學習記錄4-C呼叫Java靜態方法修改靜態

一. jni互動相關-方法簽名 方法簽名在jni的使用中經常都會用到,在java中會有過載,那麼定位到一個方法的方式:類+方法名稱+方法簽名,那麼我們先學習下簽名規則: 基本型別簽名: 咱們基本型別有各自的簽名,如下表 型別名

java併發學習()-----多執行緒的團隊協作:同步控制

Java高併發學習(八)-------多執行緒的團隊協作:同步控制   同步控制是併發程式必不可少的重要手段。之前介紹的synchronized關鍵字就是一種最簡單的控制方法。同時,wait()和notify()方法起到了執行緒等待和通知的作用。這些工具對於實現複雜的多

java核心學習() java基礎庫之Scanner

system.in 網站 文檔 數據源 java程序 官方網站 基本功 簡單 方法 從第九節開始,對java的基礎類庫進行初步學習,這些基礎類其中的一些內容是java程序員的基本功 言歸正傳,開始學習。對於java類庫的具體使用方法,可以在IDE裏面查看源碼和註釋,也可以在

Java Netty 學習() - ChannelPipeline

上一篇文章學習了Channel,它遮蔽了許多底層的java.net.Socket的操作, 那麼,當有了資料流之後,就到了如何處理它的時候,那麼本篇文章先看ChannelPipeline和ChannelHandler。 概念 Netty裡面的ChannelPipeline就類似於一

java併發學習--執行緒池(一)

關於java中的執行緒池,我一開始覺得就是為了避免頻繁的建立和銷燬執行緒吧,先建立一定量的執行緒,然後再進行復用。但是要具體說一下如何做到的,自己又說不出一個一二三來了,這大概就是自己的學習習慣流於表面,不經常深入的結果吧。所以這裡決定系統的學習一下執行緒池的相關知識。   自己稍微總結了一下,

java併發學習1---ThreadpoolExecutor

開發過程中,合理地使用執行緒池可以帶來3個好處: 降低資源消耗:通過重複利用已建立的執行緒降低執行緒建立和銷燬造成的消耗。 提高響應速度:當任務到達時,任務可以不需要等到執行緒建立就能立即執行。 提高執行緒的可管理性:執行緒是稀缺資源,如果無限制地建立,不僅會消耗系統資源,還會降

java併發學習02---ReadWriteLock 讀寫鎖

同步器--讀寫鎖 java併發包中幫我們進行了一系列的封裝,之前的重入鎖需要我們手動的加鎖和釋放鎖,而同步器只需要我們簡單的去使用就可以了。 之前我們無論是使用synchronized還是ReentrantLock,都是對整個操作進行了加鎖,但我們可以想象到,如果兩個執行緒都進行的知識讀取的操作,那麼實際

java高階反射之獲取(三)

package com.jk.fs; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /**  *  &nbs

java併發學習03---CountDownLatch 和 CyclicBarrier

CountDownLatch,顧名思義就是一個倒計時器。(其實Latch的意思是門閂,這個詞的本意是不斷的計數減一,減到0了就開啟門閂放行,但通常我們還是叫它倒計時器) 這個倒計時器和我們傳統意義上的倒計時器並不完全一樣,這個倒計時器的意思是,一開始規定幾個執行緒(比如說我們這裡一開始有10個執行緒),那麼

java併發學習04---Future模式

由於Future模式在平常看到的程式碼中用的比較多,所以就先小結下這個模式,後面再來看併發容器中的集合類。   JDK中的Future模式: Future,既是未來的意思,那麼這個模式的意思呢,就是說這個任務我現在並不會馬上做完,你現在先去做點別的,等我做好了再通知你,聯絡“未來“ 

【轉載】解決Java關鍵字作為json資料名問題

轉自:https://blog.csdn.net/jjj11223344/article/details/79957559   在java命名規範中,我們不能採用Java關鍵字如 public、static等命名,但是在服務端資料命名時我們往往會採用某一欄位的英文來命名,這有時候就

Redis基於eval的多原子增量計算

目錄 目錄 1 1. 前言 1 2. 優點 1 3. 方法一:使用struct 2 3.1. 設定初始值(覆蓋原有的,如果存在) 2 3.2. 查詢k1的值 2 3.3. 設定初始值(覆蓋原有的,如果存在) 2

Java序列化排除指定

前提是使用的是Serializable進行序列化和反序列化的   1.使用變數修飾符  transient 這裡列印password 的值是為 空的   2.使用關鍵字 static 第二種這個很容易產生誤解,content在輸出

JAVA使用Gson排除特定

1. 忽略值為NULL Gson gson = new GsonBuilder().serializeNulls().create(); 2. 使用Java關鍵字transient class Item { String name; public tran

Java JDBC中,MySQL型別到JAVA型別的轉換

1. 概述   在使用Java JDBC時,你是否有過這樣的疑問:MySQL裡的資料型別到底該選擇哪種Java型別與之對應?本篇將為你揭開這個答案。 2. 型別對映    java.sql.Types定義了常用資料庫(MySQL、Oracle、DB2等)所用到的資料型別

Java併發程式設計:volatile關鍵字解析-原子性,可見性,有序性

volatile這個關鍵字可能很多朋友都聽說過,或許也都用過。在Java 5之前,它是一個備受爭議的關鍵字,因為在程式中使用它往往會導致出人意料的結果。在Java 5之後,volatile關鍵字才得以重獲生機。   volatile關鍵字雖然從字面上理解起來比較簡單,但是要用好不是一件容易的事情