摘要:方法由兩個(gè)參數(shù),表示期望的值,表示要給設(shè)置的新值。操作包含三個(gè)操作數(shù)內(nèi)存位置預(yù)期原值和新值。如果處的值尚未同時(shí)更改,則操作成功。中就使用了這樣的操作。上面操作還有一點(diǎn)是將事務(wù)范圍縮小了,也提升了系統(tǒng)并發(fā)處理的性能。
這是java高并發(fā)系列第21篇文章。
本文主要內(nèi)容從網(wǎng)站計(jì)數(shù)器實(shí)現(xiàn)中一步步引出CAS操作
介紹java中的CAS及CAS可能存在的問(wèn)題
悲觀鎖和樂(lè)觀鎖的一些介紹及數(shù)據(jù)庫(kù)樂(lè)觀鎖的一個(gè)常見(jiàn)示例
使用java中的原子操作實(shí)現(xiàn)網(wǎng)站計(jì)數(shù)器功能
我們需要解決的問(wèn)題需求:我們開(kāi)發(fā)了一個(gè)網(wǎng)站,需要對(duì)訪(fǎng)問(wèn)量進(jìn)行統(tǒng)計(jì),用戶(hù)每次發(fā)一次請(qǐng)求,訪(fǎng)問(wèn)量+1,如何實(shí)現(xiàn)呢?
下面我們來(lái)模仿有100個(gè)人同時(shí)訪(fǎng)問(wèn),并且每個(gè)人對(duì)咱們的網(wǎng)站發(fā)起10次請(qǐng)求,最后總訪(fǎng)問(wèn)次數(shù)應(yīng)該是1000次。實(shí)現(xiàn)訪(fǎng)問(wèn)如下。
方式1代碼如下:
package com.itsoku.chat20; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * 跟著阿里p7學(xué)并發(fā),微信公眾號(hào):javacode2018 */ public class Demo1 { //訪(fǎng)問(wèn)次數(shù) static int count = 0; //模擬訪(fǎng)問(wèn)一次 public static void request() throws InterruptedException { //模擬耗時(shí)5毫秒 TimeUnit.MILLISECONDS.sleep(5); count++; } public static void main(String[] args) throws InterruptedException { long starTime = System.currentTimeMillis(); int threadSize = 100; CountDownLatch countDownLatch = new CountDownLatch(threadSize); for (int i = 0; i < threadSize; i++) { Thread thread = new Thread(() -> { try { for (int j = 0; j < 10; j++) { request(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { countDownLatch.countDown(); } }); thread.start(); } countDownLatch.await(); long endTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + ",耗時(shí):" + (endTime - starTime) + ",count=" + count); } }
輸出:
main,耗時(shí):138,count=975
代碼中的count用來(lái)記錄總訪(fǎng)問(wèn)次數(shù),request()方法表示訪(fǎng)問(wèn)一次,內(nèi)部休眠5毫秒模擬內(nèi)部耗時(shí),request方法內(nèi)部對(duì)count++操作。程序最終耗時(shí)1秒多,執(zhí)行還是挺快的,但是count和我們期望的結(jié)果不一致,我們期望的是1000,實(shí)際輸出的是973(每次運(yùn)行結(jié)果可能都不一樣)。
分析一下問(wèn)題出在哪呢?
代碼中采用的是多線(xiàn)程的方式來(lái)操作count,count++會(huì)有線(xiàn)程安全問(wèn)題,count++操作實(shí)際上是由以下三步操作完成的:
獲取count的值,記做A:A=count
將A的值+1,得到B:B = A+1
讓B賦值給count:count = B
如果有A、B兩個(gè)線(xiàn)程同時(shí)執(zhí)行count++,他們同時(shí)執(zhí)行到上面步驟的第1步,得到的count是一樣的,3步操作完成之后,count只會(huì)+1,導(dǎo)致count只加了一次,從而導(dǎo)致結(jié)果不準(zhǔn)確。
那么我們應(yīng)該怎么做的呢?
對(duì)count++操作的時(shí)候,我們讓多個(gè)線(xiàn)程排隊(duì)處理,多個(gè)線(xiàn)程同時(shí)到達(dá)request()方法的時(shí)候,只能允許一個(gè)線(xiàn)程可以進(jìn)去操作,其他的線(xiàn)程在外面候著,等里面的處理完畢出來(lái)之后,外面等著的再進(jìn)去一個(gè),這樣操作count++就是排隊(duì)進(jìn)行的,結(jié)果一定是正確的。
我們前面學(xué)了synchronized、ReentrantLock可以對(duì)資源加鎖,保證并發(fā)的正確性,多線(xiàn)程情況下可以保證被鎖的資源被串行訪(fǎng)問(wèn),那么我們用synchronized來(lái)實(shí)現(xiàn)一下。
使用synchronized實(shí)現(xiàn)代碼如下:
package com.itsoku.chat20; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; /** * 跟著阿里p7學(xué)并發(fā),微信公眾號(hào):javacode2018 */ public class Demo2 { //訪(fǎng)問(wèn)次數(shù) static int count = 0; //模擬訪(fǎng)問(wèn)一次 public static synchronized void request() throws InterruptedException { //模擬耗時(shí)5毫秒 TimeUnit.MILLISECONDS.sleep(5); count++; } public static void main(String[] args) throws InterruptedException { long starTime = System.currentTimeMillis(); int threadSize = 100; CountDownLatch countDownLatch = new CountDownLatch(threadSize); for (int i = 0; i < threadSize; i++) { Thread thread = new Thread(() -> { try { for (int j = 0; j < 10; j++) { request(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { countDownLatch.countDown(); } }); thread.start(); } countDownLatch.await(); long endTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + ",耗時(shí):" + (endTime - starTime) + ",count=" + count); } }
輸出:
main,耗時(shí):5563,count=1000
程序中request方法使用synchronized關(guān)鍵字,保證了并發(fā)情況下,request方法同一時(shí)刻只允許一個(gè)線(xiàn)程訪(fǎng)問(wèn),request加鎖了相當(dāng)于串行執(zhí)行了,count的結(jié)果和我們預(yù)期的結(jié)果一致,只是耗時(shí)比較長(zhǎng),5秒多。
方式3我們?cè)诳匆幌耤ount++操作,count++操作實(shí)際上是被拆分為3步驟執(zhí)行:
1. 獲取count的值,記做A:A=count 2. 將A的值+1,得到B:B = A+1 3. 讓B賦值給count:count = B
方式2中我們通過(guò)加鎖的方式讓上面3步驟同時(shí)只能被一個(gè)線(xiàn)程操作,從而保證結(jié)果的正確性。
我們是否可以只在第3步加鎖,減少加鎖的范圍,對(duì)第3步做以下處理:
獲取鎖 第三步獲取一下count最新的值,記做LV 判斷LV是否等于A,如果相等,則將B的值賦給count,并返回true,否者返回false 釋放鎖
如果我們發(fā)現(xiàn)第3步返回的是false,我們就再次去獲取count,將count賦值給A,對(duì)A+1賦值給B,然后再將A、B的值帶入到上面的過(guò)程中執(zhí)行,直到上面的結(jié)果返回true為止。
我們用代碼來(lái)實(shí)現(xiàn),如下:
package com.itsoku.chat20; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * 跟著阿里p7學(xué)并發(fā),微信公眾號(hào):javacode2018 */ public class Demo3 { //訪(fǎng)問(wèn)次數(shù) volatile static int count = 0; //模擬訪(fǎng)問(wèn)一次 public static void request() throws InterruptedException { //模擬耗時(shí)5毫秒 TimeUnit.MILLISECONDS.sleep(5); int expectCount; do { expectCount = getCount(); } while (!compareAndSwap(expectCount, expectCount + 1)); } /** * 獲取count當(dāng)前的值 * * @return */ public static int getCount() { return count; } /** * @param expectCount 期望count的值 * @param newCount 需要給count賦的新值 * @return */ public static synchronized boolean compareAndSwap(int expectCount, int newCount) { //判斷count當(dāng)前值是否和期望的expectCount一樣,如果一樣將newCount賦值給count if (getCount() == expectCount) { count = newCount; return true; } return false; } public static void main(String[] args) throws InterruptedException { long starTime = System.currentTimeMillis(); int threadSize = 100; CountDownLatch countDownLatch = new CountDownLatch(threadSize); for (int i = 0; i < threadSize; i++) { Thread thread = new Thread(() -> { try { for (int j = 0; j < 10; j++) { request(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { countDownLatch.countDown(); } }); thread.start(); } countDownLatch.await(); long endTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + ",耗時(shí):" + (endTime - starTime) + ",count=" + count); } }
輸出:
main,耗時(shí):116,count=1000
代碼中用了volatile關(guān)鍵字修飾了count,可以保證count在多線(xiàn)程情況下的可見(jiàn)性。關(guān)于volatile關(guān)鍵字的使用,也是非常非常重要的,前面有講過(guò),不太了解的朋友可以去看一下:volatile與Java內(nèi)存模型
咱們?cè)倏匆幌麓a,compareAndSwap方法,我們給起個(gè)簡(jiǎn)稱(chēng)吧叫CAS,這個(gè)方法有什么作用呢?這個(gè)方法使用synchronized修飾了,能保證此方法是線(xiàn)程安全的,多線(xiàn)程情況下此方法是串行執(zhí)行的。方法由兩個(gè)參數(shù),expectCount:表示期望的值,newCount:表示要給count設(shè)置的新值。方法內(nèi)部通過(guò)getCount()獲取count當(dāng)前的值,然后與期望的值expectCount比較,如果期望的值和count當(dāng)前的值一致,則將新值newCount賦值給count。
再看一下request()方法,方法中有個(gè)do-while循環(huán),循環(huán)內(nèi)部獲取count當(dāng)前值賦值給了expectCount,循環(huán)結(jié)束的條件是compareAndSwap返回true,也就是說(shuō)如果compareAndSwap如果不成功,循環(huán)再次獲取count的最新值,然后+1,再次調(diào)用compareAndSwap方法,直到compareAndSwap返回成功為止。
代碼中相當(dāng)于將count++拆分開(kāi)了,只對(duì)最后一步加鎖了,減少了鎖的范圍,此代碼的性能是不是比方式2快不少,還能保證結(jié)果的正確性。大家是不是感覺(jué)這個(gè)compareAndSwap方法挺好的,這東西確實(shí)很好,java中已經(jīng)給我們提供了CAS的操作,功能非常強(qiáng)大,我們繼續(xù)向下看。
CASCAS,compare and swap的縮寫(xiě),中文翻譯成比較并交換。
CAS 操作包含三個(gè)操作數(shù) —— 內(nèi)存位置(V)、預(yù)期原值(A)和新值(B)。 如果內(nèi)存位置的值與預(yù)期原值相匹配,那么處理器會(huì)自動(dòng)將該位置值更新為新值 。否則,處理器不做任何操作。無(wú)論哪種情況,它都會(huì)在 CAS 指令之前返回該 位置的值。(在 CAS 的一些特殊情況下將僅返回 CAS 是否成功,而不提取當(dāng)前 值。)CAS 有效地說(shuō)明了“我認(rèn)為位置 V 應(yīng)該包含值 A;如果包含該值,則將 B 放到這個(gè)位置;否則,不要更改該位置,只告訴我這個(gè)位置現(xiàn)在的值即可?!?/strong>
通常將 CAS 用于同步的方式是從地址 V 讀取值 A,執(zhí)行多步計(jì)算來(lái)獲得新 值 B,然后使用 CAS 將 V 的值從 A 改為 B。如果 V 處的值尚未同時(shí)更改,則 CAS 操作成功。
系統(tǒng)底層進(jìn)行CAS操作的時(shí)候,會(huì)判斷當(dāng)前系統(tǒng)是否為多核系統(tǒng),如果是就給總線(xiàn)加鎖,只有一個(gè)線(xiàn)程會(huì)對(duì)總線(xiàn)加鎖成功,加鎖成功之后會(huì)執(zhí)行cas操作,也就是說(shuō)CAS的原子性實(shí)際上是CPU實(shí)現(xiàn)的, 其實(shí)在這一點(diǎn)上還是有排他鎖的.,只是比起用synchronized, 這里的排他時(shí)間要短的多, 所以在多線(xiàn)程情況下性能會(huì)比較好。
java中提供了對(duì)CAS操作的支持,具體在sun.misc.Unsafe類(lèi)中,聲明如下:
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5); public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
上面三個(gè)方法都是類(lèi)似的,主要對(duì)4個(gè)參數(shù)做一下說(shuō)明。
var1:表示要操作的對(duì)象var2:表示要操作對(duì)象中屬性地址的偏移量
var4:表示需要修改數(shù)據(jù)的期望的值
var5:表示需要修改為的新值
JUC包中大部分功能都是依靠CAS操作完成的,所以這塊也是非常重要的,有關(guān)Unsafe類(lèi),下篇文章會(huì)具體講解。
synchronized、ReentrantLock這種獨(dú)占鎖屬于悲觀鎖,它是在假設(shè)需要操作的代碼一定會(huì)發(fā)生沖突的,執(zhí)行代碼的時(shí)候先對(duì)代碼加鎖,讓其他線(xiàn)程在外面等候排隊(duì)獲取鎖。悲觀鎖如果鎖的時(shí)間比較長(zhǎng),會(huì)導(dǎo)致其他線(xiàn)程一直處于等待狀態(tài),像我們部署的web應(yīng)用,一般部署在tomcat中,內(nèi)部通過(guò)線(xiàn)程池來(lái)處理用戶(hù)的請(qǐng)求,如果很多請(qǐng)求都處于等待獲取鎖的狀態(tài),可能會(huì)耗盡tomcat線(xiàn)程池,從而導(dǎo)致系統(tǒng)無(wú)法處理后面的請(qǐng)求,導(dǎo)致服務(wù)器處于不可用狀態(tài)。
除此之外,還有樂(lè)觀鎖,樂(lè)觀鎖的含義就是假設(shè)系統(tǒng)沒(méi)有發(fā)生并發(fā)沖突,先按無(wú)鎖方式執(zhí)行業(yè)務(wù),到最后了檢查執(zhí)行業(yè)務(wù)期間是否有并發(fā)導(dǎo)致數(shù)據(jù)被修改了,如果有并發(fā)導(dǎo)致數(shù)據(jù)被修改了 ,就快速返回失敗,這樣的操作使系統(tǒng)并發(fā)性能更高一些。cas中就使用了這樣的操作。
關(guān)于樂(lè)觀鎖這塊,想必大家在數(shù)據(jù)庫(kù)中也有用到過(guò),給大家舉個(gè)例子,可能以后會(huì)用到。
如果你們的網(wǎng)站中有調(diào)用支付寶充值接口的,支付寶那邊充值成功了會(huì)回調(diào)商戶(hù)系統(tǒng),商戶(hù)系統(tǒng)接收到請(qǐng)求之后怎么處理呢?假設(shè)用戶(hù)通過(guò)支付寶在商戶(hù)系統(tǒng)中充值100,支付寶那邊會(huì)從用戶(hù)賬戶(hù)中扣除100,商戶(hù)系統(tǒng)接收到支付寶請(qǐng)求之后應(yīng)該在商戶(hù)系統(tǒng)中給用戶(hù)賬戶(hù)增加100,并且把訂單狀態(tài)置為成功。
處理過(guò)程如下:
開(kāi)啟事務(wù) 獲取訂單信息 if(訂單狀態(tài)==待處理){ 給用戶(hù)賬戶(hù)增加100 將訂單狀態(tài)更新為成功 } 返回訂單處理成功 提交事務(wù)
由于網(wǎng)絡(luò)等各種問(wèn)題,可能支付寶回調(diào)商戶(hù)系統(tǒng)的時(shí)候,回調(diào)超時(shí)了,支付寶又發(fā)起了一筆回調(diào)請(qǐng)求,剛好這2筆請(qǐng)求同時(shí)到達(dá)上面代碼,最終結(jié)果是給用戶(hù)賬戶(hù)增加了200,這樣事情就搞大了,公司蒙受損失,嚴(yán)重點(diǎn)可能讓公司就此倒閉了。
那我們可以用樂(lè)觀鎖來(lái)實(shí)現(xiàn),給訂單表加個(gè)版本號(hào)version,要求每次更新訂單數(shù)據(jù),將版本號(hào)+1,那么上面的過(guò)程可以改為:
獲取訂單信息,將version的值賦值給V_A if(訂單狀態(tài)==待處理){ 開(kāi)啟事務(wù) 給用戶(hù)賬戶(hù)增加100 update影響行數(shù) = update 訂單表 set version = version + 1 where id = 訂單號(hào) and version = V_A; if(update影響行數(shù)==1){ 提交事務(wù) }else{ 回滾事務(wù) } } 返回訂單處理成功
上面的update語(yǔ)句相當(dāng)于我們說(shuō)的CAS操作,執(zhí)行這個(gè)update語(yǔ)句的時(shí)候,多線(xiàn)程情況下,數(shù)據(jù)庫(kù)會(huì)對(duì)當(dāng)前訂單記錄加鎖,保證只有一條執(zhí)行成功,執(zhí)行成功的,影響行數(shù)為1,執(zhí)行失敗的影響行數(shù)為0,根據(jù)影響行數(shù)來(lái)決定提交還是回滾事務(wù)。上面操作還有一點(diǎn)是將事務(wù)范圍縮小了,也提升了系統(tǒng)并發(fā)處理的性能。這個(gè)知識(shí)點(diǎn)希望你們能get到。
CAS 的問(wèn)題cas這么好用,那么有沒(méi)有什么問(wèn)題呢?還真有
ABA問(wèn)題
CAS需要在操作值的時(shí)候檢查下值有沒(méi)有發(fā)生變化,如果沒(méi)有發(fā)生變化則更新,但是如果一個(gè)值原來(lái)是A,變成了B,又變成了A,那么使用CAS進(jìn)行檢查時(shí)會(huì)發(fā)現(xiàn)它的值沒(méi)有發(fā)生變化,但是實(shí)際上卻變化了。這就是CAS的ABA問(wèn)題。 常見(jiàn)的解決思路是使用版本號(hào)。在變量前面追加上版本號(hào),每次變量更新的時(shí)候把版本號(hào)加一,那么A-B-A 就會(huì)變成1A-2B-3A。 目前在JDK的atomic包里提供了一個(gè)類(lèi)AtomicStampedReference來(lái)解決ABA問(wèn)題。這個(gè)類(lèi)的compareAndSet方法作用是首先檢查當(dāng)前引用是否等于預(yù)期引用,并且當(dāng)前標(biāo)志是否等于預(yù)期標(biāo)志,如果全部相等,則以原子方式將該引用和該標(biāo)志的值設(shè)置為給定的更新值。
循環(huán)時(shí)間長(zhǎng)開(kāi)銷(xiāo)大
上面我們說(shuō)過(guò)如果CAS不成功,則會(huì)原地循環(huán)(自旋操作),如果長(zhǎng)時(shí)間自旋會(huì)給CPU帶來(lái)非常大的執(zhí)行開(kāi)銷(xiāo)。并發(fā)量比較大的情況下,CAS成功概率可能比較低,可能會(huì)重試很多次才會(huì)成功。
使用JUC中的類(lèi)實(shí)現(xiàn)計(jì)數(shù)器juc框架中提供了一些原子操作,底層是通過(guò)Unsafe類(lèi)中的cas操作實(shí)現(xiàn)的。通過(guò)原子操作可以保證數(shù)據(jù)在并發(fā)情況下的正確性。
此處我們使用java.util.concurrent.atomic.AtomicInteger類(lèi)來(lái)實(shí)現(xiàn)計(jì)數(shù)器功能,AtomicInteger內(nèi)部是采用cas操作來(lái)保證對(duì)int類(lèi)型數(shù)據(jù)增減操作在多線(xiàn)程情況下的正確性。
計(jì)數(shù)器代碼如下:
package com.itsoku.chat20; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * 跟著阿里p7學(xué)并發(fā),微信公眾號(hào):javacode2018 */ public class Demo4 { //訪(fǎng)問(wèn)次數(shù) static AtomicInteger count = new AtomicInteger(); //模擬訪(fǎng)問(wèn)一次 public static void request() throws InterruptedException { //模擬耗時(shí)5毫秒 TimeUnit.MILLISECONDS.sleep(5); //對(duì)count原子+1 count.incrementAndGet(); } public static void main(String[] args) throws InterruptedException { long starTime = System.currentTimeMillis(); int threadSize = 100; CountDownLatch countDownLatch = new CountDownLatch(threadSize); for (int i = 0; i < threadSize; i++) { Thread thread = new Thread(() -> { try { for (int j = 0; j < 10; j++) { request(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { countDownLatch.countDown(); } }); thread.start(); } countDownLatch.await(); long endTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + ",耗時(shí):" + (endTime - starTime) + ",count=" + count); } }
輸出:
main,耗時(shí):119,count=1000
耗時(shí)很短,并且結(jié)果和期望的一致。
關(guān)于原子類(lèi)操作,都位于java.util.concurrent.atomic包中,下篇文章我們主要來(lái)介紹一下這些常用的類(lèi)及各自的使用場(chǎng)景。
java高并發(fā)系列java高并發(fā)系列 - 第1天:必須知道的幾個(gè)概念
java高并發(fā)系列 - 第2天:并發(fā)級(jí)別
java高并發(fā)系列 - 第3天:有關(guān)并行的兩個(gè)重要定律
java高并發(fā)系列 - 第4天:JMM相關(guān)的一些概念
java高并發(fā)系列 - 第5天:深入理解進(jìn)程和線(xiàn)程
java高并發(fā)系列 - 第6天:線(xiàn)程的基本操作
java高并發(fā)系列 - 第7天:volatile與Java內(nèi)存模型
java高并發(fā)系列 - 第8天:線(xiàn)程組
java高并發(fā)系列 - 第9天:用戶(hù)線(xiàn)程和守護(hù)線(xiàn)程
java高并發(fā)系列 - 第10天:線(xiàn)程安全和synchronized關(guān)鍵字
java高并發(fā)系列 - 第11天:線(xiàn)程中斷的幾種方式
java高并發(fā)系列 - 第12天JUC:ReentrantLock重入鎖
java高并發(fā)系列 - 第13天:JUC中的Condition對(duì)象
java高并發(fā)系列 - 第14天:JUC中的LockSupport工具類(lèi),必備技能
java高并發(fā)系列 - 第15天:JUC中的Semaphore(信號(hào)量)
java高并發(fā)系列 - 第16天:JUC中等待多線(xiàn)程完成的工具類(lèi)CountDownLatch,必備技能
java高并發(fā)系列 - 第17天:JUC中的循環(huán)柵欄CyclicBarrier的6種使用場(chǎng)景
java高并發(fā)系列 - 第18天:JAVA線(xiàn)程池,這一篇就夠了
java高并發(fā)系列 - 第19天:JUC中的Executor框架詳解1
java高并發(fā)系列 - 第20天:JUC中的Executor框架詳解2
阿里p7一起學(xué)并發(fā),公眾號(hào):路人甲java,每天獲取最新文章!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/75815.html
摘要:我們需要先了解這些概念。在中,其表現(xiàn)在對(duì)于共享變量的某些操作,是不可分的,必須連續(xù)的完成。有序性有序性指的是程序按照代碼的先后順序執(zhí)行。 JMM(java內(nèi)存模型),由于并發(fā)程序要比串行程序復(fù)雜很多,其中一個(gè)重要原因是并發(fā)程序中數(shù)據(jù)訪(fǎng)問(wèn)一致性和安全性將會(huì)受到嚴(yán)重挑戰(zhàn)。如何保證一個(gè)線(xiàn)程可以看到正確的數(shù)據(jù)呢?這個(gè)問(wèn)題看起來(lái)很白癡。對(duì)于串行程序來(lái)說(shuō),根本就是小菜一碟,如果你讀取一個(gè)變量,這個(gè)...
摘要:有時(shí)候,由于初期考慮不周,或者后期的需求變化,一些普通變量可能也會(huì)有線(xiàn)程安全的需求。它可以讓你在不改動(dòng)或者極少改動(dòng)原有代碼的基礎(chǔ)上,讓普通的變量也享受操作帶來(lái)的線(xiàn)程安全性,這樣你可以修改極少的代碼,來(lái)獲得線(xiàn)程安全的保證。 有時(shí)候,由于初期考慮不周,或者后期的需求變化,一些普通變量可能也會(huì)有線(xiàn)程安全的需求。如果改動(dòng)不大,我們可以簡(jiǎn)單地修改程序中每一個(gè)使用或者讀取這個(gè)變量的地方。但顯然,這...
摘要:我們繼續(xù)看代碼的意思是這個(gè)是一段內(nèi)嵌匯編代碼。也就是在語(yǔ)言中使用匯編代碼。就是匯編版的比較并交換。就是保證在多線(xiàn)程情況下,不阻塞線(xiàn)程的填充和消費(fèi)。微觀上看匯編的是實(shí)現(xiàn)操作系統(tǒng)級(jí)別的原子操作的基石。 原文地址:https://www.xilidou.com/2018/02/01/java-cas/ CAS 是現(xiàn)代操作系統(tǒng),解決并發(fā)問(wèn)題的一個(gè)重要手段,最近在看 eureka 的源碼的時(shí)候。...
摘要:在本例中,講述的無(wú)鎖來(lái)自于并發(fā)包我們將這個(gè)無(wú)鎖的稱(chēng)為。在這里,我們使用二維數(shù)組來(lái)表示的內(nèi)部存儲(chǔ),如下變量存放所有的內(nèi)部元素。為什么使用二維數(shù)組去實(shí)現(xiàn)一個(gè)一維的呢這是為了將來(lái)進(jìn)行動(dòng)態(tài)擴(kuò)展時(shí)可以更加方便。 我們已經(jīng)比較完整得介紹了有關(guān)無(wú)鎖的概念和使用方法。相對(duì)于有鎖的方法,使用無(wú)鎖的方式編程更加考驗(yàn)一個(gè)程序員的耐心和智力。但是,無(wú)鎖帶來(lái)的好處也是顯而易見(jiàn)的,第一,在高并發(fā)的情況下,它比有鎖...
摘要:有三種狀態(tài)運(yùn)行關(guān)閉終止。類(lèi)類(lèi),提供了一系列工廠方法用于創(chuàng)建線(xiàn)程池,返回的線(xiàn)程池都實(shí)現(xiàn)了接口。線(xiàn)程池的大小一旦達(dá)到最大值就會(huì)保持不變,在提交新任務(wù),任務(wù)將會(huì)進(jìn)入等待隊(duì)列中等待。此線(xiàn)程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。 這是java高并發(fā)系列第19篇文章。 本文主要內(nèi)容 介紹Executor框架相關(guān)內(nèi)容 介紹Executor 介紹ExecutorService 介紹線(xiàn)程池ThreadP...
閱讀 2457·2021-10-13 09:40
閱讀 3345·2019-08-30 13:46
閱讀 1129·2019-08-29 14:05
閱讀 2964·2019-08-29 12:48
閱讀 3663·2019-08-26 13:28
閱讀 2156·2019-08-26 11:34
閱讀 2291·2019-08-23 18:11
閱讀 1167·2019-08-23 12:26