日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
并發(fā)與高并發(fā)系列之線程安全性之原子性

本文轉(zhuǎn)載自微信公眾號「安琪拉的博客」,作者安琪拉。轉(zhuǎn)載本文請聯(lián)系安琪拉的博客公眾號。

大家好,我是安琪拉,這是并發(fā)編程的第五集,完整大綱如下:

面試官:你好,你先自我介紹一下吧。

安琪拉:面試官你好,我是草叢三婊,最強(qiáng)中單,火球擁有者、不焚者,安琪拉,這是我的簡歷,請過目。

面試官:聽前一個面試官說你Java并發(fā)這塊掌握的不錯,我們深入的交流一下;

安琪拉:好好好,可以交流的深入一點

面試官:什么是線程安全性?

安琪拉:這個問題第一次被問,但是個好問題。

當(dāng)多個線程訪問某個類時,不管運(yùn)行環(huán)境采用何種調(diào)度方式或者這些進(jìn)程將如何交替執(zhí)行,并且在主調(diào)代碼中不需要任何額外的協(xié)同或者同步,這個類都能表現(xiàn)出正確的行為,那么這個類是線程安全的。

面試官:線程安全性有哪三大特點? 或者說線程不安全是由于什么引起的?

安琪拉:【太老套了吧,能不能來點新的】

線程不安全的原因:

當(dāng)前的一個操作可能不是原子的,執(zhí)行過程中會被打斷,其他線程有能力修改共享變量的值,同時存在線程修改的值不是立即對其他線程可見的,因為線程有自己的執(zhí)行空間,另外一點就是存在程序可能存在亂序執(zhí)行的情況,單線程沒問題,但是多個線程同時執(zhí)行,線程共享的數(shù)據(jù)會出現(xiàn)錯亂,以上說的自己問題歸納出線程安全需要保證的三個特性:

  • 原子性

提供互斥訪問、同一時刻只能有一個線程在操作

  • 可見性

一個線程對主內(nèi)存的修改可以及時地被其他線程看到

  • 有序性

有序性是指程序在執(zhí)行的時候,程序的代碼執(zhí)行順序和語句的順序是一致的。(你可能會想難道還有不一致的,是的,因為存在指令重排序,為什么會有指令重排,因為性能優(yōu)化的需要,比如把多次訪問主存合并到一起執(zhí)行比計算和訪問主存交替訪問更高效),重排序過程不會影響到單線程程序的執(zhí)行,卻會影響到多線程并發(fā)執(zhí)行的正確性。

面試官:那你用過java.util.concurrent.atomic包下原子性相關(guān)的類嗎?

安琪拉:用過的,Java提供了很多AtomicXXX相關(guān)的原子類,如下圖所示:

面試官:能舉個例子說明下用法嗎?

安琪拉:比如存在并發(fā),計數(shù)的場景,以netty為例,它的線程池工廠類如下:

nextId就是 AtomicInteger類型的。每次創(chuàng)建線程給線程命名的時候, 代碼如下:

 
 
 
 
  1. public Thread newThread(Runnable r) {
  2.   Thread t = this.newThread(new DefaultThreadFactory.DefaultRunnableDecorator(r), this.prefix + this.nextId.incrementAndGet());
  3.   try {
  4.     if (t.isDaemon()) {
  5.       if (!this.daemon) {
  6.         t.setDaemon(false);
  7.       }
  8.     } else if (this.daemon) {
  9.       t.setDaemon(true);
  10.     }
  11.     if (t.getPriority() != this.priority) {
  12.       t.setPriority(this.priority);
  13.     }
  14.   } catch (Exception var4) {
  15.   }
  16.   return t;
  17. }

通過 incrementAndGet 實現(xiàn)原子性的 +1。

面試官:如果不用AtomicInteger,就用普通的int 會有什么后果?

安琪拉:首先我們知道 +1 操作不是原子性的,可以分成這么幾條指令:取數(shù)指令,將數(shù)據(jù)壓入操作數(shù)棧,執(zhí)行+1操作,賦值。

關(guān)于指令這塊,扔個藍(lán)。我們編譯一段Java code 看一下。

代碼和字節(jié)碼指令分別為:

 
 
 
 
  1. public static int add(int a,int b){
  2.   int c = 0;
  3.   c = a + b;
  4.   return c;
  5. }

指令,對應(yīng)的操作解釋也有,如下:

 
 
 
 
  1. public static int add(int, int);
  2.     Code:
  3.        0: iconst_0  //初始化常量0壓入操作數(shù)棧頂
  4.        1: istore_2  //彈出操作數(shù)棧棧頂元素,保存到局部變量表第2個位置
  5.        2: iload_0   //復(fù)制a變量的值入棧
  6.        3: iload_1   //復(fù)制b變量的值入棧
  7.        4: iadd      //執(zhí)行加操作,相加結(jié)果放在棧頂
  8.        5: istore_2  //彈出操作數(shù)棧棧頂元素,保存到局部變量表第2個位置
  9.        6: iload_2   //復(fù)制局部變量表第2個位置的值入棧
  10.        7: ireturn   //彈棧,返回結(jié)果

寫這么多就是為了讓大家明白 a += 1 這種操作它不是原子的,是有多條指令組成,真的不容易,快給我點個贊,好心人的藍(lán)buff

面試官:那能跟我講下Atomic 的實現(xiàn)原理嗎?

安琪拉:【要開始卷了,到安琪拉最愛的源碼環(huán)節(jié)】

 
 
 
 
  1. /**
  2.      * Atomically increments by one the current value.
  3.      *
  4.      * @return the updated value
  5.      */
  6. public final int incrementAndGet() {
  7.   return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
  8. }

代碼很短,注釋就一句話,原子性的增加當(dāng)前值。

繼續(xù)下探:

 
 
 
 
  1. public final int getAndAddInt(Object var1, long var2, int var4) {
  2.   int var5;
  3.   do {
  4.     var5 = this.getIntVolatile(var1, var2);
  5.   } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
  6.   return var5;
  7. }

入?yún)⑹侨齻€值:var1、var2、var4 ,我們先看下這三個值分別是什么?

Val1:this ,也就是AtomicInteger 對象nextId

Val2:valueOffset 看下代碼,我另外畫了個圖,我們知道一個對象存儲空間由對象頭和成員變量組成的,那valueOffset 就是成員變量value 在AtomicInteger 對象中的偏移量。

初學(xué)者可能會問,函數(shù)放在哪呢?函數(shù)都放在方法區(qū),因為是屬于類的,不是對象私有的。

 
 
 
 
  1. private static final Unsafe unsafe = Unsafe.getUnsafe();
  2. private static final long valueOffset;
  3. static {
  4.   try {
  5.     valueOffset = unsafe.objectFieldOffset
  6.       (AtomicInteger.class.getDeclaredField("value"));
  7.   } catch (Exception ex) { throw new Error(ex); }
  8. }
  9. private volatile int value;

Val4:1

那開始詳細(xì)解釋下,下面這段代碼:

compareAndSwapInt 方法:比較val1(AtomicInteger對象)的var2(valueOffset偏移量)的值與var5(原始值)是否相等,如果相等,讓值更新成var5(原始值) + val4(1)

 
 
 
 
  1. //val1: nextId  val2: valueOffset val4: 1
  2. public final int getAndAddInt(Object var1, long var2, int var4) {
  3.   int var5; //臨時變量
  4.   do {
  5.     var5 = this.getIntVolatile(var1, var2); //這是個native方法,獲取value的值
  6.     //比較val1(AtomicInteger對象)的var2(valueOffset偏移量)的值與var5(原始值)是否相等,如果相等,讓值更新成var5(原始值) + val4(1)
  7.   } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); 
  8.   
  9.   return var5;
  10. }
  11. public native int getIntVolatile(Object var1, long var2);

compareAndSwapInt 就是Java中非常重要,也是非常出名的CAS操作,比較并交換,并發(fā)底層框架用到的地方很多。

compareAndSwapInt 會返回CAS支持狀態(tài),如果執(zhí)行失敗,會循環(huán)執(zhí)行,直到成功。

失敗的原因一般是同時有別的線程修改了這個變量的值,所以比較的時候不相等,下次執(zhí)行會獲取最新值執(zhí)行CAS。

。。。。嚶嚶嚶,打字好累啊,先寫到這,要去吃自助餐了,明天再寫可見性和有序性。


網(wǎng)站名稱:并發(fā)與高并發(fā)系列之線程安全性之原子性
本文鏈接:http://m.5511xx.com/article/cdopdij.html