日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

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

新聞中心

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

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

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

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

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

面試官:聽(tīng)前一個(gè)面試官說(shuō)你Java并發(fā)這塊掌握的不錯(cuò),我們深入的交流一下;

安琪拉:好好好,可以交流的深入一點(diǎn)

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

安琪拉:這個(gè)問(wèn)題第一次被問(wèn),但是個(gè)好問(wèn)題。

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

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

安琪拉:【太老套了吧,能不能來(lái)點(diǎn)新的】

線程不安全的原因:

當(dāng)前的一個(gè)操作可能不是原子的,執(zhí)行過(guò)程中會(huì)被打斷,其他線程有能力修改共享變量的值,同時(shí)存在線程修改的值不是立即對(duì)其他線程可見(jiàn)的,因?yàn)榫€程有自己的執(zhí)行空間,另外一點(diǎn)就是存在程序可能存在亂序執(zhí)行的情況,單線程沒(méi)問(wèn)題,但是多個(gè)線程同時(shí)執(zhí)行,線程共享的數(shù)據(jù)會(huì)出現(xiàn)錯(cuò)亂,以上說(shuō)的自己?jiǎn)栴}歸納出線程安全需要保證的三個(gè)特性:

  • 原子性

提供互斥訪問(wèn)、同一時(shí)刻只能有一個(gè)線程在操作

  • 可見(jiàn)性

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

  • 有序性

有序性是指程序在執(zhí)行的時(shí)候,程序的代碼執(zhí)行順序和語(yǔ)句的順序是一致的。(你可能會(huì)想難道還有不一致的,是的,因?yàn)榇嬖谥噶钪嘏判颍瑸槭裁磿?huì)有指令重排,因?yàn)樾阅軆?yōu)化的需要,比如把多次訪問(wèn)主存合并到一起執(zhí)行比計(jì)算和訪問(wèn)主存交替訪問(wèn)更高效),重排序過(guò)程不會(huì)影響到單線程程序的執(zhí)行,卻會(huì)影響到多線程并發(fā)執(zhí)行的正確性。

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

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

面試官:能舉個(gè)例子說(shuō)明下用法嗎?

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

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

 
 
 
 
  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. }

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

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

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

關(guān)于指令這塊,扔個(gè)藍(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. }

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

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

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

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

安琪拉:【要開(kāi)始卷了,到安琪拉最愛(ài)的源碼環(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)⑹侨齻€(gè)值:var1、var2、var4 ,我們先看下這三個(gè)值分別是什么?

Val1:this ,也就是AtomicInteger 對(duì)象nextId

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

初學(xué)者可能會(huì)問(wèn),函數(shù)放在哪呢?函數(shù)都放在方法區(qū),因?yàn)槭菍儆陬惖?,不是?duì)象私有的。

 
 
 
 
  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

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

compareAndSwapInt 方法:比較val1(AtomicInteger對(duì)象)的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; //臨時(shí)變量
  4.   do {
  5.     var5 = this.getIntVolatile(var1, var2); //這是個(gè)native方法,獲取value的值
  6.     //比較val1(AtomicInteger對(duì)象)的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 會(huì)返回CAS支持狀態(tài),如果執(zhí)行失敗,會(huì)循環(huán)執(zhí)行,直到成功。

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

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


分享名稱:并發(fā)與高并發(fā)系列之線程安全性之原子性
轉(zhuǎn)載來(lái)源:http://www.dlmjj.cn/article/cdopdij.html