摘要:即使是在多個線程一起執行的時候,一個操作一旦開始,就不會被其他線程干擾。另外是一個變量,在內存中可見,因此可以保證任何時刻任何線程總能拿到該變量的最新值。
</>復制代碼
個人覺得這一節掌握基本的使用即可!
本節思維導圖:
1 Atomic 原子類介紹Atomic 翻譯成中文是原子的意思。在化學上,我們知道原子是構成一般物質的最小單位,在化學反應中是不可分割的。在我們這里 Atomic 是指一個操作是不可中斷的。即使是在多個線程一起執行的時候,一個操作一旦開始,就不會被其他線程干擾。
所以,所謂原子類說簡單點就是具有原子/原子操作特征的類。
并發包 java.util.concurrent 的原子類都存放在java.util.concurrent.atomic下,如下圖所示。
根據操作的數據類型,可以將JUC包中的原子類分為4類
基本類型
使用原子的方式更新基本類型
AtomicInteger:整形原子類
AtomicLong:長整型原子類
AtomicBoolean :布爾型原子類
數組類型
使用原子的方式更新數組里的某個元素
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 ar = new AtomicReference();
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 方法相同。運行上面的代碼后的輸出結果如下:
5 對象的屬性修改類型原子類 5.1 對象的屬性修改類型原子類介紹</>復制代碼
Daisy
20
如果需要原子更新某個類里的某個字段時,需要用到對象的屬性修改類型原子類。
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 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
【強烈推薦】最后,給大家推薦一下阿里云最新雙11福利活動(僅限阿里云新用戶購買,老用戶拉新用戶可以獲得返現紅包,后續有機會平分百萬紅包),優惠力度非常非常非常大,另外加入拼團,后續還有機會平分100w紅包!目前我的戰隊已經有50多位新人了,現在是折上5折了也就是1折購買,已經達到了最低折扣!!!!!!。 劃重點了: 1核2G云服務器1年僅需99.5元!!!1核2G云服務器3年僅需298.50元!!!一個月僅需8.2元 該折扣僅限新人!這是我的團隊拼團地址:https://m.aliyun.com/act/team1111/#/share?params=N.FF7yxCciiM.hf47liqn !
另外,老用戶可以加入我的戰隊幫忙拉新,拉新你可以獲得什么福利呢?①即時紅包,即拆即用(最低紅包10元,最高1111元);②瓜分百萬紅包的機會(目前我的戰隊已經有29位新人,所以沖進前100的可能性非常大!沖進之后即可瓜分百萬紅包!)③返現獎勵,如果你邀請了新人你會獲得返現獎勵,返現獎勵直接到你的賬戶!(我希望我的團隊最后能夠沖進前100,別的不多說!!!誠信!)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71965.html
摘要:整個包,按照功能可以大致劃分如下鎖框架原子類框架同步器框架集合框架執行器框架本系列將按上述順序分析,分析所基于的源碼為。后,根據一系列常見的多線程設計模式,設計了并發包,其中包下提供了一系列基礎的鎖工具,用以對等進行補充增強。 showImg(https://segmentfault.com/img/remote/1460000016012623); 本文首發于一世流云專欄:https...
摘要:線程的這種交叉操作會導致線程不安全。原子操作是在多線程環境下避免數據不一致必須的手段。如果聲明一個域為一些情況就可以確保多線程訪問到的變量是最新的。并發要求一個線程對對象進行了操作,對象發生了變化,這種變化應該對其他線程是可見的。 雖是讀書筆記,但是如轉載請注明出處 http://segmentfault.com/blog/exploring/ .. 拒絕伸手復制黨 一個問題: ...
摘要:今天給大家總結一下,面試中出鏡率很高的幾個多線程面試題,希望對大家學習和面試都能有所幫助。指令重排在單線程環境下不會出先問題,但是在多線程環境下會導致一個線程獲得還沒有初始化的實例。使用可以禁止的指令重排,保證在多線程環境下也能正常運行。 下面最近發的一些并發編程的文章匯總,通過閱讀這些文章大家再看大廠面試中的并發編程問題就沒有那么頭疼了。今天給大家總結一下,面試中出鏡率很高的幾個多線...
摘要:方法由兩個參數,表示期望的值,表示要給設置的新值。操作包含三個操作數內存位置預期原值和新值。如果處的值尚未同時更改,則操作成功。中就使用了這樣的操作。上面操作還有一點是將事務范圍縮小了,也提升了系統并發處理的性能。 這是java高并發系列第21篇文章。 本文主要內容 從網站計數器實現中一步步引出CAS操作 介紹java中的CAS及CAS可能存在的問題 悲觀鎖和樂觀鎖的一些介紹及數據庫...
摘要:本身不直接支持指針的操作,所以這也是該類命名為的原因之一。中的許多方法,內部其實都是類在操作。 showImg(https://segmentfault.com/img/remote/1460000016012251); 本文首發于一世流云的專欄:https://segmentfault.com/blog... 一、Unsafe簡介 在正式的開講 juc-atomic框架系列之前,有...
閱讀 2511·2021-09-09 09:33
閱讀 2871·2019-08-30 15:56
閱讀 3155·2019-08-30 14:21
閱讀 903·2019-08-30 13:01
閱讀 867·2019-08-26 18:27
閱讀 3591·2019-08-26 13:47
閱讀 3457·2019-08-26 10:26
閱讀 1592·2019-08-23 18:38