第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

原子操作之 AtomicReference

1. 前言

今天為大家介紹原子操作之 AtomicReference。此工具位于 java.util.concurrent.atomic 包中。

本節(jié)先介紹什么是原子引用,接著展示 AtomicReference 工具類的最基本用法,之后給出 AtomicReference 工具類最常用的場合說明,然后通過簡單的編碼實(shí)現(xiàn)一個(gè)實(shí)際案例,最后帶領(lǐng)大家熟悉 AtomicReference 最常用的一些編程方法,讓大家進(jìn)一步加深對 AtomicReference 工具類的理解。

下面我們正式開始介紹吧。

2. 概念介紹

本節(jié)介紹的 AtomicReference 工具類直譯為 “原子引用”。原子操作的概念我們在之前的章節(jié)中已經(jīng)介紹過了,那什么是引用呢?

引用就是為對象另起一個(gè)名字,引用對象本身指向被引用對象,對引用對象的操作都會反映到被引用對象上。在 Java 中,引用對象本身存儲的是被引用對象的 “索引值”。如果對引用概念還是比較模糊,請查閱 Java 基礎(chǔ)語法知識復(fù)習(xí)。

AtomicReference 工具類和 AtomicInteger 工具類很相似,只是 AtomicInteger 工具類是對基本類型的原子封裝,而 AtomicReference 工具類是對引用類型的原子封裝。我們用一張?jiān)韴D展示其基本邏輯。
圖片描述

我們看下面 AtomicReference 工具類的基本用法。

3. 基本用法


// 由于AtomicReference是對一個(gè)對象引用的原子封裝,所以首先創(chuàng)建一個(gè)對象
Car car1 = new Car(100, 10);

// 接著使用構(gòu)造方法創(chuàng)建一個(gè) AtomicReference 對象。
AtomicReference<Car> atomicReference = new AtomicReference<>(car);

...
// 當(dāng)前如果是car1對象時(shí),以原子方式變更引用為car2對象,當(dāng)結(jié)果是true時(shí)則更新成功
Car car2 = new Car(200, 2);
Boolean bool = atomicReference.compareAndSet(car1, car2)
...

是不是很簡單,那 AtomicReference 在我們?nèi)粘?shí)踐中,到底應(yīng)該應(yīng)用在哪些場合比較合適呢?下面我們給出最常用的場景說明。

4. 常用場景

AtomicReference 和 AtomicInteger 非常類似,不同之處就在于 AtomicInteger 是對整數(shù)的封裝,且每次只能對一個(gè)整數(shù)進(jìn)行封裝,而 AtomicReference 則是對普通的對象引用的封裝,可將多個(gè)變量作為一個(gè)整體對象,操控多個(gè)屬性的原子性的并發(fā)類。

下面我們用 AtomicReference 工具類實(shí)現(xiàn)生活中汽車牌照競拍的例子:假設(shè)總共有 10 位客戶參與競拍,每位客戶只有一次競拍機(jī)會,競拍是資格競拍不以競拍價(jià)格為目的。請看下面的代碼。

5. 場景案例

import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceTest {

    // 代表待拍的車牌
    private static CarLicenseTag carLicenseTag = new CarLicenseTag(80000);    
    // 創(chuàng)建一個(gè) AtomicReference 對象,對車牌對象做原子引用封裝
    private static AtomicReference<CarLicenseTag> carLicenseTagAtomicReference = new AtomicReference<>(carLicenseTag);
    public static void main(String[] args) {
        // 定義5個(gè)客戶進(jìn)行競拍
        for(int i=1; i<=5; i++) {
            AuctionCustomer carAuctionCustomer = new AuctionCustomer(carLicenseTagAtomicReference, carLicenseTag, i);
            // 開始競拍
            new Thread(carAuctionCustomer).start();
        }
    }
}

/**
 * 車牌
 */
public class CarLicenseTag {
    // 每張車牌牌號事先是固定的
    private String licenseTagNo = "滬X66666";
    // 車牌的最新拍賣價(jià)格
    private double price = 80000.00;
    public CarLicenseTag(double price) {
        this.price += price;
    }
    public String toString() {
        return "CarLicenseTag{ licenseTagNo='" + licenseTagNo + ", price=" + price + '}';
    }
}

每個(gè)客戶是如何動作呢,看下面的代碼。

import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;

public class AuctionCustomer implements Runnable {
    private AtomicReference<CarLicenseTag> carLicenseTagReference;
    private CarLicenseTag carLicenseTag;
    private String customerNo;
    public AuctionCustomer(AtomicReference<CarLicenseTag> carLicenseTagReference, CarLicenseTag carLicenseTag, int customerNo) {
        this.carLicenseTagReference = carLicenseTagReference;
        this.carLicenseTag = carLicenseTag;
        this.customerNo = "第" + customerNo + "位客戶";
    }

    public void run() {
        // 客戶競拍行為 (模擬競拍思考準(zhǔn)備時(shí)間4秒鐘)
        try {
            Thread.sleep(new Random().nextInt(4000));
        } catch (Exception e) {}

        // 舉牌更新最新的競拍價(jià)格
        // 此處做原子引用更新
        boolean bool = carLicenseTagReference.compareAndSet(carLicenseTag,
                new CarLicenseTag(new Random().nextInt(1000)));
        System.out.println("第" + customerNo + "位客戶競拍" + bool + " 當(dāng)前的競拍信息" + carLicenseTagReference.get().toString());
    }
}

運(yùn)行后運(yùn)行結(jié)果如下。

...
第第1位客戶位客戶競拍true 當(dāng)前的競拍信息CarLicenseTag{ licenseTagNo='滬X66666, price=80405.0}
第第5位客戶位客戶競拍false 當(dāng)前的競拍信息CarLicenseTag{ licenseTagNo='滬X66666, price=80405.0}
第第3位客戶位客戶競拍false 當(dāng)前的競拍信息CarLicenseTag{ licenseTagNo='滬X66666, price=80405.0}
第第2位客戶位客戶競拍false 當(dāng)前的競拍信息CarLicenseTag{ licenseTagNo='滬X66666, price=80405.0}
第第4位客戶位客戶競拍false 當(dāng)前的競拍信息CarLicenseTag{ licenseTagNo='滬X66666, price=80405.0}
...

至此,大家對 AtomicReference 已經(jīng)有了初步的理解,接下來我們繼續(xù)豐富對 AtomicReference 工具類的認(rèn)識。

6. 核心方法介紹

除過上面代碼中使用的最基本的 AtomicReference (V)、compareAndSet (int, int)、get () 方法之外,我們還再介紹兩個(gè)方法的使用。下面逐個(gè)介紹。

  1. set () 方法

可以使用不帶參數(shù)的構(gòu)造方法構(gòu)造好對象后,再使用 set () 方法設(shè)置待封裝的對象。等價(jià)于使用 AtomicReference (V) 構(gòu)造方法。

  1. getAndSet () 方法

此方法以原子方式設(shè)置為給定值,并返回舊值。邏輯等同于先調(diào)用 get () 方法再調(diào)用 set () 方法。

7. 小結(jié)

本節(jié)通過一個(gè)簡單的例子,介紹了 AtomicReference 的基本用法。其實(shí)在 java.util.concurrent.atomic 包中還提供了更多更細(xì)場景的原子操作類,此包下的大部分工具類都是基于 CAS 原理實(shí)現(xiàn),正因?yàn)槿绱?,有很多相似之處,用法大同小異,希望大家在日常研發(fā)中多比較多總結(jié),早日掌握之。