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

原子操作之 AtomicInteger

1. 前言

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

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

AtomicInteger 工具類本身使用很簡(jiǎn)單,重點(diǎn)是對(duì)常用編程方法的準(zhǔn)確理解。

下面我們正式開(kāi)始介紹吧。

2. 概念解釋

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

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

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

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

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

3. 基本用法

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

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

4. 常用場(chǎng)景

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

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

5. 場(chǎng)景案例

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerTest {

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

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

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

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

在 TicketOffice 類中,首先通過(guò) get () 獲取了當(dāng)前可售的總票數(shù),在有余票的情況下繼續(xù)售票。然后隨機(jī)休眠代替售票過(guò)程,最后使用 decrementAndGet () 嘗試出票。我們觀察一下運(yùn)行結(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é)束售票

在這個(gè)案例中,因?yàn)榇嬖诙鄠€(gè)售票窗口同時(shí)對(duì)一場(chǎng)電影進(jìn)行售票,如果不對(duì)可售票數(shù)做并發(fā)售票控制,很可能會(huì)出現(xiàn)多賣出票的尷尬。例子中沒(méi)有直接使用 synchronized 關(guān)鍵字做同步控制,而是使用 JDK 封裝好的 AtomicInteger 原子工具類實(shí)現(xiàn)了并發(fā)控制整型變量的操作,是不是很方便呢。

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

6. 核心方法介紹

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

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

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

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 個(gè)方法是先獲取值,之后再對(duì)原值做增 1 操作,注意獲取的值是變更之前的值。而第 2 個(gè)方法正好相反,是先對(duì)原值做增 1 的操作之后再獲取更新過(guò)的值。

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