摘要:如果程序是在多處理器上運(yùn)行,就為指令加上前綴。關(guān)于的鎖有如下種處理器自動(dòng)保證基本內(nèi)存操作的原子性首先處理器會(huì)自動(dòng)保證基本的內(nèi)存操作的原子性。使用緩存鎖保證原子性第二個(gè)機(jī)制是通過緩存鎖定保證原子性。
前言 概述
與鎖不同的是, CAS 是一種無鎖操作,一種無阻塞的算法,它實(shí)質(zhì)上不能說是一種鎖,而是將 CPU 充分利用起來的一種算法
CAS 廣泛應(yīng)用在數(shù)據(jù)結(jié)構(gòu)中,JDK中的 java.util.concurrent 并發(fā)包就是在其操作下建立的
眾所周知,JAVA 作為一門高級(jí)語言,是不支持一些底層處理的,例如指針,內(nèi)存控制等等,但大家可以看看 sun.misc.Unsafe 類,也是在它的支持下,JAVA 具備了對(duì)硬件級(jí)別原子操作的支持,這個(gè)包有很多應(yīng)用,例如 java.util.concurrent.atomic 包下的原子類都是基于其實(shí)現(xiàn) CAS 操作的
我的測(cè)試下,當(dāng)線程數(shù)量不大時(shí),CAS 要快于鎖,但線程數(shù)量很多很多時(shí),CAS 卻更慢了
參考http://blog.csdn.net/hsuxu/ar...
http://www.cnblogs.com/mickol...
舉個(gè)例子,如 i++,它是分三步的
先取內(nèi)存中的 i
再將 i 加上 1
最后將加完后的值賦給內(nèi)存中的 i
但若在其賦值前,i 的內(nèi)存值已經(jīng)被其他線程修改,此處肯定會(huì)丟失數(shù)據(jù),也就是說它是線程不安全的
如果給這個(gè)操作加上鎖,那代價(jià)未免也太大了,CAS 便可以更快地解決這個(gè)問題
原理CAS 的原理其實(shí)很簡(jiǎn)單,主要分三個(gè)參數(shù)
內(nèi)存值 - 內(nèi)存里的實(shí)際值
舊期望值 - 操作前的值
新值 - 操作后的值
CAS 的操作簡(jiǎn)而言之就是 compare and swap
將內(nèi)存值與舊期望值比較
若相等,則說明此值在操作中沒有被其他線程改變過,并將新值賦給內(nèi)存值
若不等,則說明此值在操作中已經(jīng)被其他線程改變過,并一直自旋直到相等
ABA 問題簡(jiǎn)而言之,ABA 問題就是,比如我取內(nèi)存值 A,在我比較之前,它被其他人改成了 B,然后又被其他人改回了 A,而我之后再做比較,相等成立,但是又會(huì)造成數(shù)據(jù)丟失的問題
CAS 真正比較的應(yīng)該是 值的狀態(tài),而不是值的大小,我可以給值附帶一個(gè) 版本號(hào),然后更新時(shí)對(duì)版本號(hào)進(jìn)行值大小的 CAS 操作,或者附帶一個(gè) 時(shí)間戳也是一樣的,像現(xiàn)在數(shù)據(jù)庫大部分都是采用附加版本號(hào)的方法
大家也可以看看 java.util.concurrent.atomic.AtomicStampedReference 是怎么解決 ABA 問題的,在這里就不講述了
缺點(diǎn)如果每個(gè)人都在自旋,CPU 的開銷將是巨大的,關(guān)于本人的測(cè)試,當(dāng)線程數(shù)量很多很多時(shí),CAS 會(huì)更慢就是這個(gè)原因
AtomicInteger我們來看看 java.util.concurrent.atomic.AtomicInteger 是怎么實(shí)現(xiàn)原子操作的,其主要成員如下
// Unsafe 類實(shí)例,具體的在下一篇文章詳細(xì)講,這里先跳過 private static final Unsafe unsafe = Unsafe.getUnsafe(); // 值偏移量 private static final long valueOffset; // 內(nèi)部封裝值 private volatile int value; // unsafe 初始化 static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } }
我們常用的 incrementAndGet 方法如下,在這里是直接調(diào)用 Unsafe 的 native 方法,實(shí)現(xiàn)硬件級(jí)別的原子操作,底層是用匯編實(shí)現(xiàn)的
public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; }
這個(gè)本地方法在openjdk中依次調(diào)用的c++代碼為:unsafe.cpp,atomic.cpp和atomicwindowsx86.inline.hpp。這個(gè)本地方法的最終實(shí)現(xiàn)在openjdk的如下位置:openjdk-7-fcs-src-b147-27jun2011openjdkhotspotsrcoscpuwindowsx86vm atomicwindowsx86.inline.hpp(對(duì)應(yīng)于windows操作系統(tǒng),X86處理器)。下面是對(duì)應(yīng)于intel x86處理器的源代碼的片段:
// Adding a lock prefix to an instruction on MP machine // VC++ doesn"t like the lock prefix to be on a single line // so we can"t insert a label after the lock prefix. // By emitting a lock prefix, we can define a label after it. #define LOCK_IF_MP(mp) __asm cmp mp, 0 __asm je L0 __asm _emit 0xF0 __asm L0: inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { // alternative for InterlockedCompareExchange int mp = os::is_MP(); __asm { mov edx, dest mov ecx, exchange_value mov eax, compare_value LOCK_IF_MP(mp) cmpxchg dword ptr [edx], ecx } }
如上面源代碼所示,程序會(huì)根據(jù)當(dāng)前處理器的類型來決定是否為cmpxchg指令添加lock前綴。如果程序是在多處理器上運(yùn)行,就為cmpxchg指令加上lock前綴(lock cmpxchg)。反之,如果程序是在單處理器上運(yùn)行,就省略lock前綴(單處理器自身會(huì)維護(hù)單處理器內(nèi)的順序一致性,不需要lock前綴提供的內(nèi)存屏障效果)。
CPU關(guān)于CPU的鎖有如下3種:
處理器自動(dòng)保證基本內(nèi)存操作的原子性首先處理器會(huì)自動(dòng)保證基本的內(nèi)存操作的原子性。處理器保證從系統(tǒng)內(nèi)存當(dāng)中讀取或者寫入一個(gè)字節(jié)是原子的,意思是當(dāng)一個(gè)處理器讀取一個(gè)字節(jié)時(shí),其他處理器不能訪問這個(gè)字節(jié)的內(nèi)存地址。奔騰6和最新的處理器能自動(dòng)保證單處理器對(duì)同一個(gè)緩存行里進(jìn)行16/32/64位的操作是原子的,但是復(fù)雜的內(nèi)存操作處理器不能自動(dòng)保證其原子性,比如跨總線寬度,跨多個(gè)緩存行,跨頁表的訪問。但是處理器提供總線鎖定和緩存鎖定兩個(gè)機(jī)制來保證復(fù)雜內(nèi)存操作的原子性
使用總線鎖保證原子性第一個(gè)機(jī)制是通過總線鎖保證原子性。如果多個(gè)處理器同時(shí)對(duì)共享變量進(jìn)行讀改寫(i++就是經(jīng)典的讀改寫操作)操作,那么共享變量就會(huì)被多個(gè)處理器同時(shí)進(jìn)行操作,這樣讀改寫操作就不是原子的,操作完之后共享變量的值會(huì)和期望的不一致,舉個(gè)例子:如果i=1,我們進(jìn)行兩次i++操作,我們期望的結(jié)果是3,但是有可能結(jié)果是2,如圖
原因是有可能多個(gè)處理器同時(shí)從各自的緩存中讀取變量i,分別進(jìn)行加一操作,然后分別寫入系統(tǒng)內(nèi)存當(dāng)中。那么想要保證讀改寫共享變量的操作是原子的,就必須保證CPU1讀改寫共享變量的時(shí)候,CPU2不能操作緩存了該共享變量內(nèi)存地址的緩存。
處理器使用總線鎖就是來解決這個(gè)問題的。所謂總線鎖就是使用處理器提供的一個(gè)LOCK#信號(hào),當(dāng)一個(gè)處理器在總線上輸出此信號(hào)時(shí),其他處理器的請(qǐng)求將被阻塞住,那么該處理器可以獨(dú)占使用共享內(nèi)存。
使用緩存鎖保證原子性第二個(gè)機(jī)制是通過緩存鎖定保證原子性。在同一時(shí)刻我們只需保證對(duì)某個(gè)內(nèi)存地址的操作是原子性即可,但總線鎖定把CPU和內(nèi)存之間通信鎖住了,這使得鎖定期間,其他處理器不能操作其他內(nèi)存地址的數(shù)據(jù),所以總線鎖定的開銷比較大,最近的處理器在某些場(chǎng)合下使用緩存鎖定代替總線鎖定來進(jìn)行優(yōu)化。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/67245.html
摘要:一簡(jiǎn)介單點(diǎn)登錄,簡(jiǎn)稱為,是目前比較流行的企業(yè)業(yè)務(wù)整合的解決方案之一??蛻舳藬r截未認(rèn)證的用戶請(qǐng)求,并重定向至服務(wù)端,由服務(wù)端對(duì)用戶身份進(jìn)行統(tǒng)一認(rèn)證。三搭建客戶端在官方文檔中提供了客戶端樣例,即。 一、簡(jiǎn)介 單點(diǎn)登錄(Single Sign On),簡(jiǎn)稱為 SSO,是目前比較流行的企業(yè)業(yè)務(wù)整合的解決方案之一。SSO的定義是在多個(gè)應(yīng)用系統(tǒng)中,用戶只需要登錄一次就可以訪問所有相互信任的應(yīng)用系...
摘要:所以客戶端的集成主要是單點(diǎn)登錄的集成,客戶端指定需要做安全認(rèn)證的頁面,然后的安全包檢測(cè)校驗(yàn)用戶登錄情況,并自動(dòng)與登錄頁面進(jìn)行跳轉(zhuǎn)交互。提供了很多配置的方式,有,,以及其他可查官網(wǎng)。但高度自由的一如既往的,沒有提供可視化操作的界面。 前兩篇介紹了Apereo CAS以及服務(wù)器端的安裝,但還不夠完整,服務(wù)端還沒有Application真正用起來呢!這篇文章將介紹怎么用起來 集成的目的 客戶...
摘要:客戶端與集成指定端口請(qǐng)求路徑用于單點(diǎn)退出,該過濾器用于實(shí)現(xiàn)單點(diǎn)登出功能,可選配置該過濾器用于實(shí)現(xiàn)單點(diǎn)登出功能,可選配置。該過濾器使得開發(fā)者可以通過來獲取用戶的登錄名。 CAS客戶端與SpringSecurity集成 pom.xml org.springframework spring-context 4.3.9....
JAVA單點(diǎn)登錄有好多種方式,譬如用cookie的domain做,用中間代理做等等,但都需要自行做許多開發(fā)工作。而其中耶魯大學(xué)的開源項(xiàng)目CAS提供了一個(gè)一站式解決方案,只需很少的擴(kuò)展即可輕松實(shí)現(xiàn)企業(yè)級(jí)單點(diǎn)登錄?;A(chǔ)知識(shí)網(wǎng)上其他挺多的,這里我就不詳述了。本文通過分析http請(qǐng)求過程中httpheader,cookie等數(shù)據(jù)剖析了cas(非代理模式,默認(rèn)驗(yàn)證邏輯。其他如restletAPI等可擴(kuò)展邏輯...
摘要:這種情況通常發(fā)生在反向代理的時(shí)候,前端發(fā)起請(qǐng)求代理服務(wù)器,代理服務(wù)器發(fā)起請(qǐng)求到,這時(shí)候就容易導(dǎo)致域名不一致,請(qǐng)一定要注意這點(diǎn)。 寫在最前 前后端分離其實(shí)有兩類: 開發(fā)階段使用dev-server,生產(chǎn)階段是打包成靜態(tài)文件整個(gè)放入后端項(xiàng)目中。 開發(fā)階段使用dev-server,生產(chǎn)階段是打包成靜態(tài)文件放入單獨(dú)的靜態(tài)資源服務(wù)器中,如nginx。 這兩種方案最大的區(qū)別就是生產(chǎn)階段。由于第...
摘要:這種情況通常發(fā)生在反向代理的時(shí)候,前端發(fā)起請(qǐng)求代理服務(wù)器,代理服務(wù)器發(fā)起請(qǐng)求到,這時(shí)候就容易導(dǎo)致域名不一致,請(qǐng)一定要注意這點(diǎn)。 寫在最前 前后端分離其實(shí)有兩類: 開發(fā)階段使用dev-server,生產(chǎn)階段是打包成靜態(tài)文件整個(gè)放入后端項(xiàng)目中。 開發(fā)階段使用dev-server,生產(chǎn)階段是打包成靜態(tài)文件放入單獨(dú)的靜態(tài)資源服務(wù)器中,如nginx。 這兩種方案最大的區(qū)別就是生產(chǎn)階段。由于第...
閱讀 1677·2021-11-12 10:35
閱讀 1618·2021-08-03 14:02
閱讀 2691·2019-08-30 15:55
閱讀 2033·2019-08-30 15:54
閱讀 768·2019-08-30 14:01
閱讀 2432·2019-08-29 17:07
閱讀 2259·2019-08-26 18:37
閱讀 3039·2019-08-26 16:51