欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報邀請碼778899分享:Java之線程篇六

柚子快報邀請碼778899分享:Java之線程篇六

http://yzkb.51969.com/

目錄

CAS?

CAS偽代碼

CAS的應(yīng)用

實現(xiàn)原子類?

實現(xiàn)自旋鎖

CAS的ABA問題

ABA問題導(dǎo)致BUG的例子?

相關(guān)面試題

synchronized原理

synchronized特性?

加鎖過程

相關(guān)面試題

Callable

相關(guān)面試題

JUC的常見類

ReentrantLock

ReentrantLock 和 synchronized 的區(qū)別:

原子類

信號量

相關(guān)面試題

CAS?

CAS: 全稱Compare and swap,字面意思:”比較并交換“,一個 CAS 涉及到以下操作:

我們假設(shè)內(nèi)存中的原數(shù)據(jù)V,舊的預(yù)期值A(chǔ),需要修改的新值B。 1. 比較 A 與 V 是否相等。(比較) 2. 如果比較相等,將 B 寫入 V。(交換) 3. 返回操作是否成功。

CAS偽代碼

下面寫的代碼不是原子的

,

真實的

CAS

是一個原子的硬件指令完成的

.

這個偽代碼只是輔助理解

CAS

的工作流程。

boolean CAS(address, expectValue, swapValue) {

if (&address == expectedValue) {

? &address = swapValue;

? ? ? return true;

? }

? return false;

}

CAS其實是一個cpu指令,單個的cpu指令,是原子的?。?!

就可以使用CAS完成一些操作,進一步代替“加鎖”;

基于CAS實現(xiàn)線程安全的方式也稱為“無鎖化編程”。

優(yōu)點:保證線程安全,同時避免阻塞,影響效率;

缺點:代碼會變復(fù)雜,不好理解;只能夠適用特定場景,不如加鎖方式更普遍。

CAS的應(yīng)用

實現(xiàn)原子類?

標(biāo)準(zhǔn)庫中提供了 java.util.concurrent.atomic 包, 里面的類都是基于這種方式來實現(xiàn)的.? 典型的就是 AtomicInteger 類. 其中的 getAndIncrement 相當(dāng)于 i++ 操作.

偽代碼實現(xiàn)

class AtomicInteger {

? ?private int value;

? ?public int getAndIncrement() {

? ? ? ?int oldValue = value;

? ? ? ?while ( CAS(value, oldValue, oldValue+1) != true) {

? ? ? ? ? ?oldValue = value;

? ? ? }

? ? ? ?return oldValue;

? }

}

實現(xiàn)自旋鎖

偽代碼實現(xiàn)

public class SpinLock {

? ?private Thread owner = null;

? ?public void lock(){

? ? ? ?// 通過 CAS 看當(dāng)前鎖是否被某個線程持有.

? ? ? ?// 如果這個鎖已經(jīng)被別的線程持有, 那么就自旋等待.

? ? ? ?// 如果這個鎖沒有被別的線程持有, 那么就把 owner 設(shè)為當(dāng)前嘗試加鎖的線程.

? ? ? ?while(!CAS(this.owner, null, Thread.currentThread())){

? ? ? }

? }

? ?public void unlock (){

? ? ? ?this.owner = null;

? }

}

CAS的ABA問題

ABA問題即:

假設(shè)有兩個線程t1和t2,有一個共享變量num,初值為A,接下來t1想使用CAS把num改為Z,那么就需要先讀取num的值,記錄到oldNum變量中,然后使用CAS判斷當(dāng)前num的值是否為A,如果為A,則改為Z。

但是在t1執(zhí)行上述操作之前,t2線程可能把num的值從A改為B,又從B改為A。

那么此時,線程t1無法區(qū)分當(dāng)前這個變量始終是A,還是經(jīng)歷了一個變化過程。

ABA問題導(dǎo)致BUG的例子?

假設(shè)你要去ATM取款機取錢,余額有1000,要取款500,但是取款的時候ATM機卡了一下,所以你按了兩下,假設(shè)ATM取款機按CAS方式工作,雖然你按了兩次,但是你取出的是500,不過,如果恰巧這個時候有人給你轉(zhuǎn)了500,這個時候,你按的第2下取款,使用CAS方式會發(fā)現(xiàn)余額還是1000,那么此時就會余額沒有改變,你最后會取出1000.

解決方法:引入版本號等方式

給要修改的值, 引入版本號. 在 CAS 比較數(shù)據(jù)當(dāng)前值和舊值的同時, 也要比較版本號是否符合預(yù)期.? CAS 操作在讀取舊值的同時, 也要讀取版本號.? 真正修改的時候,? 如果當(dāng)前版本號和讀到的版本號相同, 則修改數(shù)據(jù), 并把版本號 + 1. 如果當(dāng)前版本號高于讀到的版本號. 就操作失敗(認為數(shù)據(jù)已經(jīng)被修改過了).

相關(guān)面試題

1) 講解下你自己理解的 CAS 機制

全稱 Compare and swap, 即 "比較并交換". 相當(dāng)于通過一個原子的操作, 同時完成 "讀取內(nèi)存, 比較是否相等, 修改內(nèi)存" 這三個步驟. 本質(zhì)上需要 CPU 指令的支撐

2) ABA問題怎么解決?

給要修改的數(shù)據(jù)引入版本號. 在 CAS 比較數(shù)據(jù)當(dāng)前值和舊值的同時, 也要比較版本號是否符合預(yù)期.? 如果發(fā)現(xiàn)當(dāng)前版本號和之前讀到的版本號一致, 就真正執(zhí)行修改操作, 并讓版本號自增; 如果發(fā)現(xiàn)當(dāng)前版本號比之前讀到的版本號大, 就認為操作失敗.

synchronized原理

synchronized特性?

1. 開始時是樂觀鎖, 如果鎖沖突頻繁, 就轉(zhuǎn)換為悲觀鎖. 2. 開始是輕量級鎖實現(xiàn), 如果鎖被持有的時間較長, 就轉(zhuǎn)換成重量級鎖.? 3. 實現(xiàn)輕量級鎖的時候大概率用到的自旋鎖策略 4. 是一種不公平鎖 5. 是一種可重入鎖 6. 不是讀寫鎖

加鎖過程

JVM 將 synchronized 鎖分為 無鎖、偏向鎖、輕量級鎖、重量級鎖 狀態(tài)。會根據(jù)情況,進行依次升級。?

1)偏向鎖

第一個嘗試加鎖的線程, 優(yōu)先進入偏向鎖狀態(tài).? 偏向鎖不是真的 "加鎖", 只是給對象頭中做一個 "偏向鎖的標(biāo)記", 記錄這個鎖屬于哪個線程.? 如果后續(xù)沒有其他線程來競爭該鎖, 那么就不用進行其他同步操作了(避免了加鎖解鎖的開銷) 如果后續(xù)有其他線程來競爭該鎖(剛才已經(jīng)在鎖對象中記錄了當(dāng)前鎖屬于哪個線程了, 很容易識別當(dāng)前申請鎖的線程是不是之前記錄的線程), 那就取消原來的偏向鎖狀態(tài), 進入一般的輕量級鎖狀態(tài).? 偏向鎖本質(zhì)上相當(dāng)于 "延遲加鎖" . 能不加鎖就不加鎖, 盡量來避免不必要的加鎖開銷.? 但是該做的標(biāo)記還是得做的, 否則無法區(qū)分何時需要真正加鎖.

2)輕量級鎖

隨著其他線程進入競爭, 偏向鎖狀態(tài)被消除, 進入輕量級鎖狀態(tài)(自適應(yīng)的自旋鎖).? 此處的輕量級鎖就是通過 CAS 來實現(xiàn).

通過 CAS 檢查并更新一塊內(nèi)存 (比如 null => 該線程引用) 如果更新成功, 則認為加鎖成功 如果更新失敗, 則認為鎖被占用, 繼續(xù)自旋式的等待(并不放棄 CPU).?

3)重量級鎖

如果競爭進一步激烈, 自旋不能快速獲取到鎖狀態(tài), 就會膨脹為重量級鎖 此處的重量級鎖就是指用到內(nèi)核提供的 mutex .? 執(zhí)行加鎖操作, 先進入內(nèi)核態(tài).? 在內(nèi)核態(tài)判定當(dāng)前鎖是否已經(jīng)被占用 如果該鎖沒有占用, 則加鎖成功, 并切換回用戶態(tài).? 如果該鎖被占用, 則加鎖失敗. 此時線程進入鎖的等待隊列, 掛起. 等待被操作系統(tǒng)喚醒.? 經(jīng)歷了一系列的滄海桑田, 這個鎖被其他線程釋放了, 操作系統(tǒng)也想起了這個掛起的線程, 于是喚醒這個線程, 嘗試重新獲取鎖.?

鎖消除

編譯器+JVM 判斷鎖是否可消除. 如果可以, 就直接消除。

例如:單線程環(huán)境下。?

鎖粗化

一段邏輯中如果出現(xiàn)多次加鎖解鎖, 編譯器 + JVM 會自動進行鎖的粗化. ?

相關(guān)面試題

1)什么是偏向鎖?

偏向鎖不是真的加鎖

,

而只是在鎖的對象頭中記錄一個標(biāo)記

(

記錄該鎖所屬的線程

).

如果沒有其他線程參與競爭鎖,

那么就不會真正執(zhí)行加鎖操作

,

從而降低程序開銷

.

一旦真的涉及到其他的線程競爭,

再取消偏向鎖狀態(tài)

,

進入輕量級鎖狀態(tài)

.

Callable

Callable 和 Runnable 相對, 都是描述一個 "任務(wù)". Callable 描述的是帶有返回值的任務(wù),? Runnable 描述的是不帶返回值的任務(wù).? Callable 通常需要搭配 FutureTask 來使用. FutureTask 用來保存 Callable 的返回結(jié)果. 因為 Callable 往往是在另一個線程中執(zhí)行的, 啥時候執(zhí)行完并不確定.? FutureTask 就可以負責(zé)這個等待結(jié)果出來的工作?

理解FutureTask

想象去吃麻辣燙

.

當(dāng)餐點好后

,

后廚就開始做了

.

同時前臺會給你一張

"

小票

" .

這個小票就是

FutureTask.

后面我們可以隨時憑這張小票去查看自己的這份麻辣燙做出來了沒

.

代碼示例

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.FutureTask;

public class Demo30 {

public static void main(String[] args) throws ExecutionException, InterruptedException {

// 定義了任務(wù).

Callable callable = new Callable() {

@Override

public Integer call() throws Exception {

int sum = 0;

for (int i = 0; i <= 1000; i++) {

sum += i;

}

return sum;

}

};

// 把任務(wù)放到線程中進行執(zhí)行.

FutureTask futureTask = new FutureTask<>(callable);

Thread t = new Thread(futureTask);

t.start();

// 此處的 get 就能獲取到 callable 里面的返回結(jié)果.

// 由于線程是并發(fā)執(zhí)行的. 執(zhí)行到主線程的 get 的時候, t 線程可能還沒執(zhí)行完.

// 沒執(zhí)行完的話, get 就會阻塞.

System.out.println(futureTask.get());

}

}

相關(guān)面試題

介紹一下Callable是什么

Callable 是一個 interface . 相當(dāng)于把線程封裝了一個 "返回值". 方便程序猿借助多線程的方式計算結(jié)果.? Callable 和 Runnable 相對, 都是描述一個 "任務(wù)". Callable 描述的是帶有返回值的任務(wù),? Runnable 描述的是不帶返回值的任務(wù). Callable 通常需要搭配 FutureTask 來使用. FutureTask 用來保存 Callable 的返回結(jié)果. 因為 Callable 往往是在另一個線程中執(zhí)行的, 啥時候執(zhí)行完并不確定.? FutureTask 就可以負責(zé)這個等待結(jié)果出來的工作.

JUC的常見類

JUC,是java.util.concurrent的縮寫。

ReentrantLock

可重入互斥鎖. 和 synchronized 定位類似, 都是用來實現(xiàn)互斥效果, 保證線程安全.? ReentrantLock 也是可重入鎖. "Reentrant" 這個單詞的原意就是 "可重入".

ReentrantLock的用法

lock(): 加鎖, 如果獲取不到鎖就死等.? trylock(超時時間): 加鎖, 如果獲取不到鎖, 等待一定的時間之后就放棄加鎖.? unlock(): 解鎖

ReentrantLock 和 synchronized 的區(qū)別:

synchronized 是一個關(guān)鍵字, 是 JVM 內(nèi)部實現(xiàn)的(大概率是基于 C++ 實現(xiàn)). ReentrantLock 是標(biāo)準(zhǔn)庫的一個類, 在 JVM 外實現(xiàn)的(基于 Java 實現(xiàn)).? synchronized 使用時不需要手動釋放鎖. ReentrantLock 使用時需要手動釋放. 使用起來更靈活,?但是也容易遺漏 unlock.? synchronized 在申請鎖失敗時, 會死等. ReentrantLock 可以通過 trylock 的方式等待一段時間就放棄.? synchronized 是非公平鎖, ReentrantLock 默認是非公平鎖. 可以通過構(gòu)造方法傳入一個 true 開啟公平鎖模式.?

更強大的喚醒機制. synchronized 是通過 Object 的 wait / notify 實現(xiàn)等待-喚醒. 每次喚醒的是一個隨機等待的線程. ReentrantLock 搭配 Condition 類實現(xiàn)等待-喚醒, 可以更精確控制喚醒某個指定的線程.

// ReentrantLock 的構(gòu)造方法

public ReentrantLock(boolean fair) {

? ?sync = fair ? new FairSync() : new NonfairSync();

}

?如何選擇使用哪個鎖?

鎖競爭不激烈的時候, 使用 synchronized, 效率更高, 自動釋放更方便.? 鎖競爭激烈的時候, 使用 ReentrantLock, 搭配 trylock 更靈活控制加鎖的行為, 而不是死等.? 如果需要使用公平鎖, 使用 ReentrantLock.

原子類

原子類內(nèi)部用的是 CAS 實現(xiàn),所以性能要比加鎖實現(xiàn) i++ 高很多。原子類有以下幾個 AtomicBoolean AtomicInteger AtomicIntegerArray AtomicLong AtomicReference AtomicStampedReference?

ExecutorService和Executors

ExecutorService 表示一個線程池實例.? Executors 是一個工廠類, 能夠創(chuàng)建出幾種不同風(fēng)格的線程池.? ExecutorService 的 submit 方法能夠向線程池中提交若干個任務(wù).?

代碼示例

ExecutorService pool = Executors.newFixedThreadPool(10);

pool.submit(new Runnable() {

? ?@Override

? ?public void run() {

? ? ? ?System.out.println("hello");

? }

});

Executors 創(chuàng)建線程池的幾種方式:

newFixedThreadPool: 創(chuàng)建固定線程數(shù)的線程池 newCachedThreadPool: 創(chuàng)建線程數(shù)目動態(tài)增長的線程池. newSingleThreadExecutor: 創(chuàng)建只包含單個線程的線程池.? newScheduledThreadPool: 設(shè)定 延遲時間后執(zhí)行命令,或者定期執(zhí)行命令. 是進階版的 Timer.?

Executors 本質(zhì)上是 ThreadPoolExecutor 類的封裝.?

信號量

信號量(Semaphore), 用來表示 "可用資源的個數(shù)". 本質(zhì)上就是一個計數(shù)器. Semaphore 的 PV 操作中的加減計數(shù)器操作都是原子的, 可以在多線程環(huán)境下直接使用.?

acquire

方法表示申請資源

(P

操作

), release

方法表示釋放資源

(V

操作

)

?代碼示例

import java.util.concurrent.Semaphore;

public class Demo24 {

public static void main(String[] args) throws InterruptedException {

Semaphore semaphore = new Semaphore(4);

semaphore.acquire();

System.out.println("P 操作");

semaphore.acquire();

System.out.println("P 操作");

semaphore.acquire();

System.out.println("P 操作");

semaphore.acquire();

System.out.println("P 操作");

semaphore.acquire();

System.out.println("P 操作");

}

}

運行結(jié)果

相關(guān)面試題

1) 線程同步的方式有哪些?

synchronized, ReentrantLock, Semaphore 等都可以用于線程同步.

2) 為什么有了 synchronized 還需要 juc 下的 lock?

以 juc 的 ReentrantLock 為例,? synchronized 使用時不需要手動釋放鎖. ReentrantLock 使用時需要手動釋放. 使用起來更 靈活, synchronized 在申請鎖失敗時, 會死等. ReentrantLock 可以通過 trylock 的方式等待一段時 間就放棄.? synchronized 是非公平鎖, ReentrantLock 默認是非公平鎖. 可以通過構(gòu)造方法傳入一個 true 開啟公平鎖模式.? synchronized 是通過 Object 的 wait / notify 實現(xiàn)等待-喚醒. 每次喚醒的是一個隨機等待的 線程. ReentrantLock 搭配 Condition 類實現(xiàn)等待-喚醒, 可以更精確控制喚醒某個指定的線 程.?

3) AtomicInteger 的實現(xiàn)原理是什么?

基于 CAS 機制. 偽代碼如下: ?

class AtomicInteger {

private int value;

public int getAndIncrement() {

? ? int oldValue = value;

? ? while ( CAS(value, oldValue, oldValue+1) != true) {

? ? ? ? oldValue = value;

? ? }

? ? return oldValue;

}

}

柚子快報邀請碼778899分享:Java之線程篇六

http://yzkb.51969.com/

精彩內(nèi)容

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。

轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/19517772.html

發(fā)布評論

您暫未設(shè)置收款碼

請在主題配置——文章設(shè)置里上傳

掃描二維碼手機訪問

文章目錄