併發編程面試必備:JUC 中的 Atomic 原子類總結

我的以爲這一節掌握基本的使用便可!java

本節思惟導圖:數組

1 Atomic 原子類介紹

Atomic 翻譯成中文是原子的意思。在化學上,咱們知道原子是構成通常物質的最小單位,在化學反應中是不可分割的。在咱們這裏 Atomic 是指一個操做是不可中斷的。即便是在多個線程一塊兒執行的時候,一個操做一旦開始,就不會被其餘線程干擾。安全

因此,所謂原子類說簡單點就是具備原子/原子操做特徵的類。bash

併發包 java.util.concurrent 的原子類都存放在java.util.concurrent.atomic下,以下圖所示。多線程

JUC 原子類概覽

根據操做的數據類型,能夠將JUC包中的原子類分爲4類併發

基本類型this

使用原子的方式更新基本類型atom

  • AtomicInteger:整形原子類
  • AtomicLong:長整型原子類
  • AtomicBoolean :布爾型原子類

數組類型spa

使用原子的方式更新數組裏的某個元素線程

  • AtomicIntegerArray:整形數組原子類
  • AtomicLongArray:長整形數組原子類
  • AtomicReferenceArray :引用類型數組原子類

引用類型

  • AtomicReference:引用類型原子類
  • AtomicStampedRerence:原子更新引用類型裏的字段原子類
  • AtomicMarkableReference :原子更新帶有標記位的引用類型

對象的屬性修改類型

  • AtomicIntegerFieldUpdater:原子更新整形字段的更新器
  • AtomicLongFieldUpdater:原子更新長整形字段的更新器
  • AtomicStampedReference :原子更新帶有版本號的引用類型。該類將整數值與引用關聯起來,可用於解決原子的更新數據和數據的版本號,能夠解決使用 CAS 進行原子更新時可能出現的 ABA 問題。

下面咱們來詳細介紹一下這些原子類。

2 基本類型原子類

2.1 基本類型原子類介紹

使用原子的方式更新基本類型

  • AtomicInteger:整形原子類
  • AtomicLong:長整型原子類
  • AtomicBoolean :布爾型原子類

上面三個類提供的方法幾乎相同,因此咱們這裏以 AtomicInteger 爲例子來介紹。

AtomicInteger 類經常使用方法

public final int get() //獲取當前的值 public final int getAndSet(int newValue)//獲取當前的值,並設置新的值 public final int getAndIncrement()//獲取當前的值,並自增 public final int getAndDecrement() //獲取當前的值,並自減 public final int getAndAdd(int delta) //獲取當前的值,並加上預期的值 boolean compareAndSet(int expect, int update) //若是輸入的數值等於預期值,則以原子方式將該值設置爲輸入值(update) public final void lazySet(int newValue)//最終設置爲newValue,使用 lazySet 設置以後可能致使其餘線程在以後的一小段時間內仍是能夠讀到舊的值。 複製代碼

2.2 AtomicInteger 常見方法使用

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int temvalue = 0;
		AtomicInteger i = new AtomicInteger(0);
		temvalue = i.getAndSet(3);
		System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:0; i:3
		temvalue = i.getAndIncrement();
		System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:3; i:4
		temvalue = i.getAndAdd(5);
		System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:4; i:9
	}

}
複製代碼

2.3 基本數據類型原子類的優點

經過一個簡單例子帶你們看一下基本數據類型原子類的優點

①多線程環境不使用原子類保證線程安全(基本數據類型)

class Test {
        private volatile int count = 0;
        //若要線程安全執行執行count++,須要加鎖
        public synchronized void increment() {
                  count++; 
        }

        public int getCount() {
                  return count;
        }
}
複製代碼

②多線程環境使用原子類保證線程安全(基本數據類型)

class Test2 {
        private AtomicInteger count = new AtomicInteger();

        public void increment() {
                  count.incrementAndGet();
        }
      //使用AtomicInteger以後,不須要加鎖,也能夠實現線程安全。
       public int getCount() {
                return count.get();
        }
}

複製代碼

2.4 AtomicInteger 線程安全原理簡單分析

AtomicInteger 類的部分源碼:

// setup to use Unsafe.compareAndSwapInt for updates(更新操做時提供「比較並替換」的做用)
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;
複製代碼

AtomicInteger 類主要利用 CAS (compare and swap) + volatile 和 native 方法來保證原子操做,從而避免 synchronized 的高開銷,執行效率大爲提高。

CAS的原理是拿指望的值和本來的一個值做比較,若是相同則更新成新的值。UnSafe 類的 objectFieldOffset() 方法是一個本地方法,這個方法是用來拿到「原來的值」的內存地址,返回值是 valueOffset。另外 value 是一個volatile變量,在內存中可見,所以 JVM 能夠保證任什麼時候刻任何線程總能拿到該變量的最新值。

3 數組類型原子類

3.1 數組類型原子類介紹

使用原子的方式更新數組裏的某個元素

  • AtomicIntegerArray:整形數組原子類
  • AtomicLongArray:長整形數組原子類
  • AtomicReferenceArray :引用類型數組原子類

上面三個類提供的方法幾乎相同,因此咱們這裏以 AtomicIntegerArray 爲例子來介紹。

AtomicIntegerArray 類經常使用方法

public final int get(int i) //獲取 index=i 位置元素的值 public final int getAndSet(int i, int newValue)//返回 index=i 位置的當前的值,並將其設置爲新值:newValue public final int getAndIncrement(int i)//獲取 index=i 位置元素的值,並讓該位置的元素自增 public final int getAndDecrement(int i) //獲取 index=i 位置元素的值,並讓該位置的元素自減 public final int getAndAdd(int delta) //獲取 index=i 位置元素的值,並加上預期的值 boolean compareAndSet(int expect, int update) //若是輸入的數值等於預期值,則以原子方式將 index=i 位置的元素值設置爲輸入值(update) public final void lazySet(int i, int newValue)//最終 將index=i 位置的元素設置爲newValue,使用 lazySet 設置以後可能致使其餘線程在以後的一小段時間內仍是能夠讀到舊的值。 複製代碼

3.2 AtomicIntegerArray 常見方法使用

import java.util.concurrent.atomic.AtomicIntegerArray;

public class AtomicIntegerArrayTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int temvalue = 0;
		int[] nums = { 1, 2, 3, 4, 5, 6 };
		AtomicIntegerArray i = new AtomicIntegerArray(nums);
		for (int j = 0; j < nums.length; j++) {
			System.out.println(i.get(j));
		}
		temvalue = i.getAndSet(0, 2);
		System.out.println("temvalue:" + temvalue + "; i:" + i);
		temvalue = i.getAndIncrement(0);
		System.out.println("temvalue:" + temvalue + "; i:" + i);
		temvalue = i.getAndAdd(0, 5);
		System.out.println("temvalue:" + temvalue + "; i:" + i);
	}

}
複製代碼

4 引用類型原子類

4.1 引用類型原子類介紹

基本類型原子類只能更新一個變量,若是須要原子更新多個變量,須要使用 引用類型原子類。

  • AtomicReference:引用類型原子類
  • AtomicStampedRerence:原子更新引用類型裏的字段原子類
  • AtomicMarkableReference :原子更新帶有標記位的引用類型

上面三個類提供的方法幾乎相同,因此咱們這裏以 AtomicReference 爲例子來介紹。

4.2 AtomicReference 類使用示例

import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceTest {

	public static void main(String[] args) {
		AtomicReference<Person> ar = new AtomicReference<Person>();
		Person person = new Person("SnailClimb", 22);
		ar.set(person);
		Person updatePerson = new Person("Daisy", 20);
		ar.compareAndSet(person, updatePerson);

		System.out.println(ar.get().getName());
		System.out.println(ar.get().getAge());
	}
}

class Person {
	private String name;
	private int age;

	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

}
複製代碼

上述代碼首先建立了一個 Person 對象,而後把 Person 對象設置進 AtomicReference 對象中,而後調用 compareAndSet 方法,該方法就是經過經過 CAS 操做設置 ar。若是 ar 的值爲 person 的話,則將其設置爲 updatePerson。實現原理與 AtomicInteger 類中的 compareAndSet 方法相同。運行上面的代碼後的輸出結果以下:

Daisy
20
複製代碼

5 對象的屬性修改類型原子類

5.1 對象的屬性修改類型原子類介紹

若是須要原子更新某個類裏的某個字段時,須要用到對象的屬性修改類型原子類。

  • AtomicIntegerFieldUpdater:原子更新整形字段的更新器
  • AtomicLongFieldUpdater:原子更新長整形字段的更新器
  • AtomicStampedReference :原子更新帶有版本號的引用類型。該類將整數值與引用關聯起來,可用於解決原子的更新數據和數據的版本號,能夠解決使用 CAS 進行原子更新時可能出現的 ABA 問題。

要想原子地更新對象的屬性須要兩步。第一步,由於對象的屬性修改類型原子類都是抽象類,因此每次使用都必須使用靜態方法 newUpdater()建立一個更新器,而且須要設置想要更新的類和屬性。第二步,更新的對象屬性必須使用 public volatile 修飾符。

上面三個類提供的方法幾乎相同,因此咱們這裏以 AtomicIntegerFieldUpdater爲例子來介紹。

5.2 AtomicIntegerFieldUpdater 類使用示例

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class AtomicIntegerFieldUpdaterTest {
	public static void main(String[] args) {
		AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");

		User user = new User("Java", 22);
		System.out.println(a.getAndIncrement(user));// 22
		System.out.println(a.get(user));// 23
	}
}

class User {
	private String name;
	public volatile int age;

	public User(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

}
複製代碼

輸出結果:

22
23
複製代碼
相關文章
相關標籤/搜索