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

原子操作之 AtomicInteger

1. 前言

從本節(jié)開始,正式帶領(lǐng)大家認識 Java 并發(fā)工具類,今天為大家介紹原子操作之 AtomicInteger。此工具位于 java.util.concurrent.atomic 包中。

本節(jié)先解釋什么是原子操作,接著介紹 AtomicInteger 工具類的最基本用法,有了這些基本認識之后,給出 AtomicInteger 工具最常用的場合說明,然后通過簡單的編碼實現(xiàn)一個實際案例,讓大家有一個理性的認識,最后帶領(lǐng)大家熟悉 AtomicInteger 最常用的一些編程方法,進一步加深對 AtomicInteger 工具類的理解。

AtomicInteger 工具類本身使用很簡單,重點是對常用編程方法的準確理解。

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

2. 概念解釋

什么是原子操作呢?所謂原子操作,就是一個獨立且不可分割的操作。

AtomicInteger 工具類提供了對整數(shù)操作的原子封裝。為什么要對整數(shù)操作進行原子封裝呢?

在 java 中,當(dāng)我們在多線程情況下,對一個整型變量做加減操作時,如果不加任何的多線程并發(fā)控制,大概率會出現(xiàn)線程安全問題,也就是說當(dāng)多線程同時操作一個整型變量的增減時,會出現(xiàn)運算結(jié)果錯誤的問題。AtomicInteger 工具類就是為了簡化整型變量的同步處理而誕生的。

大家記住,在多線程并發(fā)下,所有不是原子性的操作但需要保證原子性時,都需要進行原子操作處理,否則會出現(xiàn)線程安全問題。

概念已經(jīng)了解了,那么 AtomicInteger 工具類怎么用呢?別急,最基本的用法請看下面的描述。

3. 基本用法

// 首先創(chuàng)建一個 AtomicInteger 對象
AtomicInteger atomicInteger = new AtomicInteger();
// 在操作之前先賦值,如果不顯式賦值則值默認為 0 ,就像 int 型變量使用前做初始化賦值一樣。
atomicInteger.set(1000);
// 之后可以調(diào)用各種方法進行增減操作
...
// 獲取當(dāng)前值
atomicInteger.get();
// 先獲取當(dāng)前值,之后再對原值加100
atomicInteger.getAndAdd(100)
// 先獲取當(dāng)前值,之后再對原值減1
atomicInteger.getAndDecrement()
...

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

4. 常用場景

AtomicInteger 經(jīng)常用于多線程操作同一個整型變量時,簡化對此變量的線程安全控制的場合。當(dāng)在研發(fā)過程中遇到這些場景時,就可以考慮直接使用 AtomicInteger 工具類輔助實現(xiàn),完全可以放棄使用 synchronized 關(guān)鍵字做同步控制。

下面我們用 AtomicInteger 工具實現(xiàn)電影院某場次電影票銷售的例子。
圖片描述

5. 場景案例

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerTest {

    // 首先創(chuàng)建一個 AtomicInteger 對象
    // 代表《神龍晶晶獸》電影上午場次當(dāng)前可售的總票數(shù) 10 張
    private static AtomicInteger currentTicketCount = new AtomicInteger(10);
    // 主程序
    public static void main(String[] args) {
        // 定義3個售票窗口
        for(int i=1; i<=3; i++) {
            TicketOffice ticketOffice = new TicketOffice(currentTicketCount, i);
            // 每個售票窗口開始售票
            new Thread(ticketOffice).start();
        }
    }
}

在上面的代碼中,先創(chuàng)建了一個 AtomicInteger 對象,然后創(chuàng)建了 3 個售票窗口模擬售票動作 ,接下來每個售票窗口如何動作呢,看下面的代碼。

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

/**
 * 模擬售票窗口
 */
public class TicketOffice implements Runnable {
    // 當(dāng)前可售的總票數(shù)
    private AtomicInteger currentTicketCount;
    // 窗口名稱(編號)
    private String ticketOfficeNo;
	// 售票窗口構(gòu)造函數(shù)
    public TicketOffice(AtomicInteger currentTicketCount, int ticketOfficeNo) {
        this.currentTicketCount = currentTicketCount;
        this.ticketOfficeNo = "第" + ticketOfficeNo + "售票窗口";
    }
	// 模擬售票邏輯
    public void run() {
	    // 模擬不間斷的售票工作(生活中有工作時間段控制)
        while (true) {
            // 獲取當(dāng)前可售的總票數(shù),如果沒有余票就關(guān)閉當(dāng)前售票窗口結(jié)束售票,否則繼續(xù)售票
            if (currentTicketCount.get() < 1) {
                System.out.println("票已售完," + ticketOfficeNo + "結(jié)束售票");
                return;
            }
            // 模擬售票用時
            try {
                Thread.sleep(new Random().nextInt(1000));
            } catch (Exception e) {}
            // 當(dāng)總票數(shù)減1后不為負數(shù)時,出票成功
            int ticketIndex = currentTicketCount.decrementAndGet();
            if (ticketIndex >= 0) {
                System.out.println(ticketOfficeNo + "已出票,還剩" + ticketIndex + "張票");
            }
        }
    }
}

在 TicketOffice 類中,首先通過 get () 獲取了當(dāng)前可售的總票數(shù),在有余票的情況下繼續(xù)售票。然后隨機休眠代替售票過程,最后使用 decrementAndGet () 嘗試出票。我們觀察一下運行結(jié)果。

3售票窗口已出票,還剩9張票
第1售票窗口已出票,還剩8張票
第2售票窗口已出票,還剩7張票
第1售票窗口已出票,還剩6張票
第3售票窗口已出票,還剩5張票
第3售票窗口已出票,還剩4張票
第2售票窗口已出票,還剩3張票
第1售票窗口已出票,還剩2張票
第3售票窗口已出票,還剩1張票
第2售票窗口已出票,還剩0張票
票已售完,第2售票窗口結(jié)束售票
票已售完,第1售票窗口結(jié)束售票
票已售完,第3售票窗口結(jié)束售票

在這個案例中,因為存在多個售票窗口同時對一場電影進行售票,如果不對可售票數(shù)做并發(fā)售票控制,很可能會出現(xiàn)多賣出票的尷尬。例子中沒有直接使用 synchronized 關(guān)鍵字做同步控制,而是使用 JDK 封裝好的 AtomicInteger 原子工具類實現(xiàn)了并發(fā)控制整型變量的操作,是不是很方便呢。

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

6. 核心方法介紹

除了上面代碼中使用的最基本的 AtomicInteger (int)、AtomicInteger ()、 set () 、get () 和 decrementAndGet () 方法之外,我們還需要掌握其他幾組核心方法的使用。下面逐個介紹。

  1. getAndAdd (int) 方法與 AddAndGet (int) 方法

第 1 個方法是先獲取原值,之后再對原值做增加。注意獲取的值是變更之前的值。而第 2 個方法正好相反,是先對原值做增加操作之后再獲取更新過的值。

AtomicInteger atomicInteger = new AtomicInteger();
System.out.println(atomicInteger.get());            // 0
System.out.println(atomicInteger.getAndAdd(10));    // 0,獲取當(dāng)前值并加10
System.out.println(atomicInteger.get());            // 10
System.out.println(atomicInteger.addAndGet(20));    // 30,當(dāng)前值先加20再獲取
System.out.println(atomicInteger.get());            // 30
  1. getAndIncrement () 方法與 incrementAndGet () 方法

第 1 個方法是先獲取值,之后再對原值做增 1 操作,注意獲取的值是變更之前的值。而第 2 個方法正好相反,是先對原值做增 1 的操作之后再獲取更新過的值。

AtomicInteger atomicInteger = new AtomicInteger();
System.out.println(atomicInteger.get());  // 0
System.out.println(atomicInteger.getAndIncrement()); // 0,獲取當(dāng)前值并自增1
System.out.println(atomicInteger.get());  // 1
System.out.println(atomicInteger.incrementAndGet()); // 2,當(dāng)前值先自增1再獲取
System.out.println(atomicInteger.get());  // 2
  1. compareAndSet(int expect, int update)

原值與 expect 相比較,如果不相等則返回 false 且原有值保持不變,否則返回 true 且原值更新為 update。

AtomicInteger atomicInteger = new AtomicInteger(10);
System.out.println(atomicInteger.get()); // 10
int expect = 12;
int update = 20;
Boolean b =atomicInteger.compareAndSet(expect, update);
System.out.println(b); // 10 不等于 12 不滿足期望,所以返回false,且保持原值不變
System.out.println(atomicInteger.get());

7. 小結(jié)

本節(jié)通過一個簡單的例子,介紹了 AtomicInteger 的基本用法,另外對一些核心方法做了簡單介紹。在這個包下面,還有很多類似的工具類,也是對基本類型原子操作的封裝,如 AtomicBoolean、AtomicLong,用法大同小異,希望大家在日常研發(fā)中多比較多總結(jié),早日掌握之。