并發(fā)場景中,為了保證線程安全,也就是臨界區(qū)代碼按照我們所想的時序運行,需要進行加鎖,也就是同步控制,但是有很多情況,不需要我們自己進行同步控制,而是可以使用java自帶的并發(fā)組件,本文主要講Atomic包中提供原子操作的類,接下來我們依次分析。
相信應(yīng)該是用的最多的原子操作類了,它的主要作用是進行int類型變量的包裝,讓變量的操作都是線程安全。如何實現(xiàn)呢?最容易想到的應(yīng)該是每個方法進行加鎖控制,這是沒問題的。但juc中并非這種方式,而是使用了volatile進行可見性控制和失敗重試的CAS操作來保證成功的原子性,這也是樂觀鎖一種實現(xiàn)。 關(guān)于CAS操作:每次修改數(shù)據(jù)時,都會比較數(shù)據(jù)的原值,如果原值沒有改變,代表沒有其他線程進行并發(fā)修改,可以安全更新,如果原值改變了,代表有線程成功修改了此值,則此次更新失敗。 AtomicInteger提供了很多有用的方法,當(dāng)然我不會一一分析,就說說常用的,在源碼基礎(chǔ)是進行注釋。
/**因為value變量是用volatile修飾的所以get永遠是獲取的最新值*/ public final int get() { return value; } //原理同get() public final void set(int newValue) { value = newValue; } /**獲取原值并且設(shè)置新增,這個方法應(yīng)該用的比較少,因為這個原值可能已經(jīng)不是我們希望的了,但是這步操作會成功的改變原值*/ public final int getAndSet(int newValue) { for (;;) { int current = get(); if (compareAndSet(current, newValue)) return current; } } /**這就是cas的實現(xiàn),對先查詢后更新操作進行原子性保障,原理是直接通過內(nèi)存偏移量進行內(nèi)存最新數(shù)據(jù)的比較,如果內(nèi)存中的數(shù)據(jù)是所希望的,表示沒有其他的線程進行修改,那么就可以進行正確的更新*/public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } /**此方法應(yīng)該是最常用的,這是線程安全的i++操作,并返回原值*/ public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } } //如果i++后需要返回新值,則是下面的 public final int incrementAndGet() ; // 同理i--操作也是提供兩個 public final int getAndDecrement();//返回原值 public final int decrementAndGet();//返回新增 /**那么有i++,那么如果i+n呢,同樣也有兩個*/ public final int getAndSet(int newValue);//返回舊值 public final int addAndGet(int delta);//返回新值總的來說AtomicInteger,就是線程安全的int類型,同時提供了很多上面的常見操作,合理的使用,可以在并發(fā)編程中減輕很多的工作。
實現(xiàn)的原理和AtomicInteger一樣,底層還是用int類型,并不是boolean,就不復(fù)制源碼了,簡單的說下常用的方法:
| 方法 | 描述 |
|---|---|
| boolean get() | 獲取最新值 |
| set(boolean newValue) | 設(shè)置成newValue |
| boolean compareAndSet(boolean expect, boolean update) | cas設(shè)值 |
| boolean getAndSet(boolean newValue) | 設(shè)置newValue并返回原值 |
如果僅僅是為了賦值操作,那么也沒有必要AtomicBoolean,因為jvm虛擬機規(guī)范中規(guī)定,除了long和double類型外其他的任何類型賦值操作都是原子的,只需進行volatile修飾,保證可見性就能對單一的賦值操作進行同步保證。
AtomicLong的實現(xiàn)可以說和AtomicInteger的一模一樣,同樣那些操作,不過包裝的是long型罷了,這里就不細細分析了。
AtomicReference和AtomicBoolean的使用可以說一模一樣,只不過包裝的是對象的引用,需要注意的是,對象的比較操作不是通過==或者equals,而是比較的引用的的指針位置。
其他的數(shù)組的實現(xiàn),原理都一樣,cas操作,AtomicIntegerArray和AtomicInteger不同的地方在于,操作的方法需要帶上數(shù)組元素的位置,也就是索引,AtomicLongArray,AtomicReferenceArray同樣可以類比AtomicLong和AtomicReference,這里就不詳細說明了。
總之,在新的并發(fā)場景中,還是盡量使用線程安全的現(xiàn)有組件,安全可靠且規(guī)范。
新聞熱點
疑難解答