亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

一直使用AtomicInteger?試一試FieldUpdater

發布時間:2020-06-28 15:39:18 來源:網絡 閱讀:294 作者:艾弗森哇 欄目:編程語言

1. 背景

在進入正題之前,這里先提出一個問題,如何在多線程中去對一個數字進行+1操作?這個問題非常簡單,哪怕是Java的初學者都能回答上來,使用AtomicXXX,比如有一個int類型的自加,那么你可以使用AtomicInteger 代替int類型進行自加。

?AtomicInteger?atomicInteger?=?new?AtomicInteger();
????????atomicInteger.addAndGet(1);

如上面的代碼所示,使用addAndGet即可保證多線程中相加,具體原理在底層使用的是CAS,這里就不展開細講。基本上AtomicXXX能滿足我們的所有需求,直到前幾天一個群友(ID:皮摩)問了我一個問題,他發現在很多開源框架中,例如Netty中的AbstractReferenceCountedByteBuf 類中定義了一個refCntUpdater:

????private?static?final?AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf>?refCntUpdater;????static?{
????????AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf>?updater?=
????????????????PlatformDependent.newAtomicIntegerFieldUpdater(AbstractReferenceCountedByteBuf.class,?"refCnt");????????if?(updater?==?null)?{
????????????updater?=?AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class,?"refCnt");
????????}
????????refCntUpdater?=?updater;
????}

refCntUpdater 是Netty用來記錄ByteBuf被引用的次數,會出現并發的操作,比如增加一個引用關系,減少一個引用關系,其retain方法,實現了refCntUpdater的自增:

????private?ByteBuf?retain0(int?increment)?{????????for?(;;)?{????????????int?refCnt?=?this.refCnt;????????????final?int?nextCnt?=?refCnt?+?increment;????????????//?Ensure?we?not?resurrect?(which?means?the?refCnt?was?0)?and?also?that?we?encountered?an?overflow.
????????????if?(nextCnt?<=?increment)?{????????????????throw?new?IllegalReferenceCountException(refCnt,?increment);
????????????}????????????if?(refCntUpdater.compareAndSet(this,?refCnt,?nextCnt))?{????????????????break;
????????????}
????????}????????return?this;
????}

俗話說有因必有果,netty多費力氣做這些事必然是有自己的原因的,接下來就進入我們的正題。

2.Atomic field updater

java.util.concurrent.atomic包中有很多原子類,比如AtomicInteger,AtomicLong,LongAdder等已經是大家熟知的常用類,在這個包中還有三個類在jdk1.5中都存在了,但是經常被大家忽略,這就是fieldUpdater:

  • AtomicIntegerFieldUpdater

  • AtomicLongFieldUpdater

  • AtomicReferenceFieldUpdater

這個在代碼中不經常會有,但是有時候可以作為性能優化的工具出場,一般在下面兩種情況會使用它:

  • 你想通過正常的引用使用volatile的,比如直接在類中調用this.variable,但是你也想時不時的使用一下CAS操作或者原子自增操作,那么你可以使用fieldUpdater。

  • 當你使用AtomicXXX的時候,其引用Atomic的對象有多個的時候,你可以使用fieldUpdater節約內存開銷。

2.1 正常引用volatile變量

一般有兩種情況需要正常引用:

  1. 當代碼中引入已經正常引用,但是這個時候需要新增一個CAS的需求,我們可以將其替換AtomicXXX對象,但是之前的調用都得換成.get().set()方法,這樣做會增加不少的工作量,并且還需要大量的回歸測試。

  2. 代碼更加容易理解,在BufferedInputStream中,有一個buf數組用來表示內部緩沖區,它也是一個volatile數組,在BufferedInputStream中大多數時候只需要正常的使用這個數組緩沖區即可,在一些特殊的情況下,比如close的時候需要使用compareAndSet,我們可以使用AtomicReference,我覺得這樣做有點亂,使用fieldUpdater來說更加容易理解,

????protected?volatile?byte?buf[];????private?static?final
????????AtomicReferenceFieldUpdater<BufferedInputStream,?byte[]>?bufUpdater?=
????????AtomicReferenceFieldUpdater.newUpdater
????????(BufferedInputStream.class,??byte[].class,?"buf");????????
????public?void?close()?throws?IOException?{????????byte[]?buffer;????????while?(?(buffer?=?buf)?!=?null)?{????????????if?(bufUpdater.compareAndSet(this,?buffer,?null))?{
????????????????InputStream?input?=?in;
????????????????in?=?null;????????????????if?(input?!=?null)
????????????????????input.close();????????????????return;
????????????}????????????//?Else?retry?in?case?a?new?buf?was?CASed?in?fill()
????????}
????}

2.2 節約內存

之前說過在很多開源框架中都能看見fieldUpdater的身影,其實大部分的情況都是為了節約內存,為什么其會節約內存呢?

我們首先來看看AtomicInteger類:

public?class?AtomicInteger?extends?Number?implements?java.io.Serializable?{????private?static?final?long?serialVersionUID?=?6214790243416807050L;????//?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成員變量只有一個int value,似乎好像并沒有多出內存,但是我們的AtomicInteger是一個對象,一個對象的正確計算應該是 對象頭 + 數據大小,在64位機器上AtomicInteger對象占用內存如下:http://www.chacha8.cn/detail/1132398249.html

  • 關閉指針壓縮: 16(對象頭)+4(實例數據)=20不是8的倍數,因此需要對齊填充 16+4+4(padding)=24

  • 開啟指針壓縮(-XX:+UseCompressedOop): 12+4=16已經是8的倍數了,不需要再padding。

由于我們的AtomicInteger是一個對象,還需要被引用,那么真實的占用為:

  • 關閉指針壓縮:24 + 8 = 32

  • 開啟指針壓縮: 16 + 4 = 20

而fieldUpdater是staic final類型并不會占用我們對象的內存,所以使用fieldUpdater的話可以近似認為只用了4字節,這個再未關閉指針壓縮的情況下節約了7倍,關閉的情況下節約了4倍,這個在少量對象的情況下可能不明顯,當我們對象有幾十萬,幾百萬,或者幾千萬的時候,節約的可能就是幾十M,幾百M,甚至幾個G。

比如在netty中的AbstractReferenceCountedByteBuf,熟悉netty的同學都知道netty是自己管理內存的,所有的ByteBuf都會繼承AbstractReferenceCountedByteBuf,在netty中ByteBuf會被大量的創建,netty使用fieldUpdater用于節約內存。鄭州不孕不育醫院有哪些:http://wapyyk.39.net/zz3/zonghe/1d427.html

在阿里開源的數據庫連接池druid中也有同樣的體現,早在2012的一個pr中,就有優化內存的comment:一直使用AtomicInteger?試一試FieldUpdatercdn.xitu.io/2019/10/9/16daf616c180d4f9?w=1243&h=868&f=png&s=176909">,在druid中,有很多統計數據對象,這些對象通常會以秒級創建,分鐘級創建新的,druid通過fieldUpdater節約了大量內存:?一直使用AtomicInteger?試一試FieldUpdater

3.最后

AtomicFieldUpdater的確在我們平時使用比較少,但是其也值得我們去了解,有時候在特殊的場景下的確可以作為奇技淫巧。


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

青冈县| 金寨县| 潼关县| 韩城市| 会同县| 淳安县| 咸阳市| 汉寿县| 丰台区| 获嘉县| 梁河县| 石景山区| 兖州市| 安达市| 亚东县| 治多县| 娄底市| 连州市| 大方县| 绵竹市| 枣庄市| 泗阳县| 凉城县| 崇文区| 德保县| 新沂市| 满城县| 大荔县| 延津县| 镇坪县| 胶州市| 乳源| 周口市| 涞水县| 南昌市| 资讯| 白水县| 武宁县| 江津市| 平遥县| 安陆市|