自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

并發(fā)與高并發(fā)系列之線程安全性之原子性

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

[[409053]]

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

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

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

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

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

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

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

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

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

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

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

線程不安全的原因:

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

  • 原子性

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

  • 可見性

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

  • 有序性

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

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

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

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

安琪拉:比如存在并發(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.  
  4.   try { 
  5.     if (t.isDaemon()) { 
  6.       if (!this.daemon) { 
  7.         t.setDaemon(false); 
  8.       } 
  9.     } else if (this.daemon) { 
  10.       t.setDaemon(true); 
  11.     } 
  12.  
  13.     if (t.getPriority() != this.priority) { 
  14.       t.setPriority(this.priority); 
  15.     } 
  16.   } catch (Exception var4) { 
  17.   } 
  18.  
  19.   return t; 

通過 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; 

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

  1. public static int add(intint); 
  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)原理嗎?

安琪拉:【要開始卷了,到安琪拉最愛的源碼環(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; 

代碼很短,注釋就一句話,原子性的增加當(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.  
  7.   return var5; 

入?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ì)問,函數(shù)放在哪呢?函數(shù)都放在方法區(qū),因?yàn)槭菍儆陬惖模皇菍?duì)象私有的。

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

Val4:1

那開始詳細(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。

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

 

責(zé)任編輯:武曉燕 來源: 安琪拉的博客
相關(guān)推薦

2021-01-12 07:39:48

線程線程安全

2021-05-16 17:14:30

線程安全性

2023-01-05 12:30:32

Redis

2024-02-26 08:33:51

并發(fā)編程活躍性安全性

2009-02-12 09:55:28

2022-10-13 06:46:05

Dapr訪問控制策略

2011-01-04 16:20:26

linux安全性

2020-09-04 10:29:47

Java線程池并發(fā)

2023-04-04 07:06:21

2013-08-22 09:16:01

移動(dòng)終端安全移動(dòng)安全移動(dòng)策略

2023-04-02 09:40:29

2024-09-27 14:45:30

2009-11-30 09:41:38

2011-07-22 13:52:46

2010-06-03 15:23:48

2010-01-11 10:43:16

應(yīng)用程序安全性

2010-01-27 10:28:47

2011-08-22 14:19:23

linuxUNIXwrite

2010-01-23 20:34:02

企業(yè)網(wǎng)絡(luò)應(yīng)用程序安全

2016-11-28 09:00:10

瀏覽器瀏覽器緩存服務(wù)端
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)