成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

高并發(fā) - 基礎(chǔ)

phpmatt / 3236人閱讀

摘要:異步非阻塞方式,任務(wù)的完成的通知由其他線程發(fā)出。并發(fā)并行死鎖饑餓活鎖死鎖線程持有,線程持有。如等,在多線程情況下,該操作不是原子級(jí)別的而是原子的,所以一般用于狀態(tài)標(biāo)記。

同步/異步、阻塞/非阻塞

同步/異步是 API 被調(diào)用者的通知方式。阻塞/非阻塞則是 API 調(diào)用者的等待方式(線程掛機(jī)/不掛起)。

同步非阻塞

Future方式,任務(wù)的完成要主線程自己判斷。
如NIO,后臺(tái)有多個(gè)任務(wù)在執(zhí)行(非阻塞),主動(dòng)循環(huán)查詢(同步)多個(gè)任務(wù)的完成狀態(tài),只要有任何一個(gè)任務(wù)完成,就去處理它。這就是所謂的 “I/O 多路復(fù)用”。

同步非阻塞相比同步阻塞:
優(yōu)點(diǎn):能夠在等待任務(wù)完成的時(shí)間里干其他活了(就是 “后臺(tái)” 可以有多個(gè)任務(wù)在同時(shí)執(zhí)行)。
缺點(diǎn):任務(wù)完成的響應(yīng)延遲增大了,因?yàn)槊窟^一段時(shí)間才去輪詢一次,而任務(wù)可能在兩次輪詢之間的任意時(shí)間完成。

異步非阻塞

CompletableFuture方式,任務(wù)的完成的通知由其他線程發(fā)出。
如AIO,應(yīng)用程序發(fā)起調(diào)用,而不需要進(jìn)行輪詢,進(jìn)而處理下一個(gè)任務(wù),只需在I/O完成后通過信號(hào)或是回調(diào)將數(shù)據(jù)傳遞給應(yīng)用程序即可。

異步非阻塞相比同步非阻塞:
不需要主動(dòng)輪詢,減少CPU操作。

并發(fā)、并行

死鎖、饑餓、活鎖

死鎖

線程A持有l(wèi)ock1,線程B持有l(wèi)ock2。當(dāng)A試圖獲取lock2時(shí),此時(shí)線程B也在試圖獲取lock1。此時(shí)二者都在等待對方所持有鎖的釋放,而二者卻又都沒釋放自己所持有的鎖,這時(shí)二者便會(huì)一直阻塞下去。

饑餓

對于非公平隊(duì)列來說,線程有可能一直獲取不到對鎖的占用。

活鎖

由于某些條件沒有滿足,導(dǎo)致兩個(gè)線程一直互相“謙讓”對鎖的占用,從而一直等下去。活鎖有可能自行解開,死鎖則不能。

什么是線程安全

如果多個(gè)線程同時(shí)運(yùn)行你的代碼,每一個(gè)線程每次運(yùn)行結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,就是線程安全的。

原子性、可見性、有序性

原子性

即一個(gè)操作或者多個(gè)操作 要么全部執(zhí)行并且執(zhí)行的過程不會(huì)被任何因素打斷,要么就都不執(zhí)行。

可見性

可見性是指當(dāng)多個(gè)線程訪問同一個(gè)變量時(shí),一個(gè)線程修改了這個(gè)變量的值,其他線程能夠立即看得到修改的值。

有序性

即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。(在單線程中,編譯器對代碼的重排序沒有問題,但在多線程程序運(yùn)行就可能有問題)

x =?10;?????????//語句1
y = x;?????????//語句2
x++;???????????//語句3
x = x +?1;?????//語句4
上面4個(gè)語句只有語句1的操作具備原子性。也就是說,只有簡單的讀取、賦值(而且必須是將數(shù)字賦值給某個(gè)變量)才是原子操作。
Java內(nèi)存模型只保證了基本讀取和賦值是原子性操作,如果要實(shí)現(xiàn)更大范圍操作的原子性,可以通過synchronized和Lock來實(shí)現(xiàn)。

重排序

編譯器可能會(huì)對程序操作做重排序(為了讓CPU指令處理的流水線更加高效,減少空閑時(shí)間)。編譯器在重排序時(shí),會(huì)遵守?cái)?shù)據(jù)依賴性,不會(huì)改變存在數(shù)據(jù)依賴關(guān)系的兩個(gè)操作的執(zhí)行順序。
注意,這里所說的數(shù)據(jù)依賴性僅針對單個(gè)處理器中執(zhí)行的指令序列和單個(gè)線程中執(zhí)行的操作,不同處理器之間和不同線程之間的數(shù)據(jù)依賴性不被編譯器和處理器考慮。
所以重排序會(huì)使得多線程不安全。

關(guān)鍵字volatile

volatile修飾的變量不保留拷貝,直接訪問主內(nèi)存中的變量,即保證可見性。
volatile前面的代碼肯定在volatile之前,volatile后面的代碼肯定在volatile之后,即保證有序性。
volatile修飾的變量缺少原子性的保證。如volatile n=n+1、n++、n = m + 1 等,在多線程情況下,該操作不是原子級(jí)別的;而n=false是原子的,所以volatile一般用于狀態(tài)標(biāo)記。如果自己沒有把握,可以使用synchronized、Lock、AtomicInteger來代替volatile。

關(guān)鍵字synchronized

synchronized與static synchronized 的區(qū)別:

synchronized是對類的當(dāng)前方法的實(shí)例進(jìn)行加鎖,類的兩個(gè)不同實(shí)例的synchronized方法可以被兩個(gè)線程分別訪問。

static synchronized是類java.lang.Class對象鎖。因?yàn)楫?dāng)虛擬機(jī)加載一個(gè)類的時(shí)候,會(huì)會(huì)為這個(gè)類實(shí)例化一個(gè) java.lang.Class 對象。類的不同實(shí)例在執(zhí)行該方法時(shí)共用一個(gè)鎖。

synchronized方法只能由synchronized的方法覆蓋:
繼承時(shí)子類的覆蓋方法必須定義成synchronized。

兩個(gè)線程不能同時(shí)訪問同一對象的不同synchronized方法:
因?yàn)閟ynchronized鎖是基于對象的。但同一對象的普通方法和synchronized方法能同時(shí)被兩個(gè)線程分別訪問。

Happen-Before

?程序順序原則:一個(gè)線程內(nèi)保證語義的串行性
?volatile規(guī)則:volatile變量的寫,先發(fā)生于讀,這保證了volatile變量的可見性
?鎖規(guī)則:解鎖(unlock)必然發(fā)生在隨后的加鎖(lock)前
?傳遞性:A先于B,B先于C,那么A必然先于C
?線程的start()方法先于它的每一個(gè)動(dòng)作
?線程的所有操作先于線程的終結(jié)(Thread.join())
?線程的中斷(interrupt())先于被中斷線程的代碼
?對象的構(gòu)造函數(shù)執(zhí)行結(jié)束先于finalize()方法
這些原則保證了重排的語義是一致的。

CAS(Compare and swap)

CAS算法:CAS(V, E, N)。V表示要更新的變量,E表示預(yù)期值,N表示新值。僅當(dāng)V值等于E值時(shí),才會(huì)將V的值設(shè)為N,如果V值和E值不同,則說明已經(jīng)有其他線程做了更新,則當(dāng)前線程什么都不做。當(dāng)多個(gè)線程同時(shí)使用CAS操作一個(gè)變量時(shí),只有一個(gè)會(huì)勝出,并成功更新,其余均會(huì)失敗。失敗的線程不會(huì)被掛起,僅是被告知失敗,并且允許再次嘗試,當(dāng)然也允許失敗的線程放棄操作。

以AtomicInteger為例:

private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
private volatile int value;    //保證線程間的數(shù)據(jù)是可見的

static {
    try {    //valueOffset就是value
        valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}

public final int getAndSet(int newValue) {
    return unsafe.getAndSetInt(this, valueOffset, newValue);
}

public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

public final boolean compareAndSet(int expect, int update) {
    //對于this這個(gè)類上的偏移量為valueOffset的變量值如果與期望值expect相同,那么把這個(gè)變量的值設(shè)為update。
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

以上compareAndSet方法類似以下:

if (value ==?expect) {
    Value =?update;
    return true;
} else {
    return false;
}

那么問題來了,如果value==?expect之后,正要執(zhí)行value=?update時(shí),切換了線程更改了值,則會(huì)造成了數(shù)據(jù)不一致。但這個(gè)擔(dān)心是多余的,因?yàn)镃AS操作是原子的,中間不會(huì)有線程切換。
如何保證原子性,即一個(gè)步驟?
實(shí)際上compareAndSet()利用JNI(Java Native Interface)來執(zhí)行CPU的CMPXCHG指令,從而保證比較、交換是一步操作,即原子性操作。

CAS缺點(diǎn)

ABA問題

    static final AtomicReference ref = new AtomicReference(1);

    public final int incrementAndGet() {
        while (true) {
            int current = ref.get();
            int next = current + 1;    // 1        
        if (ref.compareAndSet(current, next)) {    // 2
                return next;
            }
        }
    }

在代碼1和代碼2之間,若其他線程將value設(shè)置為3,另一個(gè)線程又將value設(shè)置1,則CAS進(jìn)行檢查時(shí)會(huì)錯(cuò)誤的認(rèn)為值沒有發(fā)生變化,但是實(shí)際上卻變化了。這就是A變成B又變成A,即ABA問題。
解決思路就是添加版本號(hào)。在變量和版本號(hào)綁定,每次變量更新的時(shí)候把版本號(hào)加一,那么A-B-A 就會(huì)變成1A-2B-3A,當(dāng)版本號(hào)相同時(shí)才做更新值的操作。

java.util.concurrent.atomic.AtomicStampedReference可以解決ABA問題,其內(nèi)部類:

private static class Pair {
    final T reference;
    final int stamp;
    ......
 }

AtomicStampedReference的compareAndSet方法會(huì)首先檢查當(dāng)前reference是否==預(yù)期reference(內(nèi)存地址比較),并且當(dāng)前stamp是否等于預(yù)期stamp,如果都相等,則執(zhí)行Unsafe.compareAndSwapObject方法。

public boolean compareAndSet(V   expectedReference,
                             V   newReference,
                             int expectedStamp,
                             int newStamp) {
    Pair current = pair;
    return
        expectedReference == current.reference &&
        expectedStamp == current.stamp &&
        ((newReference == current.reference &&
          newStamp == current.stamp) ||
         casPair(current, Pair.of(newReference, newStamp)));    //Unsafe.compareAndSwapObject
}

循環(huán)時(shí)間長開銷大

自旋CAS如果長時(shí)間不成功,會(huì)給CPU帶來非常大的執(zhí)行開銷。如果JVM能支持處理器提供的PAUSE指令那么效率會(huì)有一定的提升,PAUSE指令提升了自旋等待循環(huán)(spin-wait loop)的性能。

只能保證一個(gè)共享變量的原子操作

對于多個(gè)共享變量操作,循環(huán)CAS就無法保證操作的原子性,這個(gè)時(shí)候就可以用鎖,或者把多個(gè)共享變量合并成一個(gè)共享變量來操作。JDK提供了AtomicReference類來保證引用對象之間的原子性,你可以把多個(gè)變量放在一個(gè)對象里來進(jìn)行CAS操作。

普通變量的原子操作
java.util.concurrent.atomic.AtomicIntegerFieldUpdater類的主要作用是讓普通變量也享受原子操作。
就比如原本有一個(gè)變量是int型,并且很多地方都應(yīng)用了這個(gè)變量,但是在某個(gè)場景下,想讓int型變成AtomicInteger,但是如果直接改類型,就要改其他地方的應(yīng)用。AtomicIntegerFieldUpdater就是為了解決這樣的問題產(chǎn)生的。

public static class V {
    volatile int score;

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }
}

public final static AtomicIntegerFieldUpdater vv = AtomicIntegerFieldUpdater.newUpdater(V.class, "score");

public static void main(String[] args) {
    final V stu = new V();
    vv.incrementAndGet(stu);
}

注:
Updater只能修改它可見范圍內(nèi)的變量。因?yàn)閁pdater使用反射得到這個(gè)變量。
變量必須是volatile類型的。
由于CAS操作會(huì)通過對象實(shí)例中的偏移量(堆內(nèi)存的偏移量)直接進(jìn)行賦值,因此,它不支持static字段(Unsafe.objectFieldOffset()不支持靜態(tài)變量)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://hztianpu.com/yun/67261.html

相關(guān)文章

  • 【Java并發(fā)】Java并發(fā)編程與并發(fā)基礎(chǔ)概念

    摘要:筆記來源并發(fā)編程與高并發(fā)解決方案并發(fā)基礎(chǔ)綜述多級(jí)緩存緩存一致性亂序執(zhí)行優(yōu)化內(nèi)存模型規(guī)定抽象結(jié)構(gòu)同步八種操作及規(guī)則并發(fā)的優(yōu)勢與風(fēng)險(xiǎn)并發(fā)與高并發(fā)基本概念基本概念并發(fā)同時(shí)擁有兩個(gè)或者多個(gè)線程,如果程序在單核處理器上運(yùn)行,多個(gè)線程將交替地?fù)Q入或者換 筆記來源:【IMOOC】Java并發(fā)編程與高并發(fā)解決方案 并發(fā)基礎(chǔ) 綜述: CPU多級(jí)緩存:緩存一致性、亂序執(zhí)行優(yōu)化 Java內(nèi)存模型:JM...

    stackfing 評論0 收藏0
  • 聊聊面試中關(guān)于并發(fā)問題的應(yīng)對方案

    摘要:這里呢,我直接給出高并發(fā)場景通常都會(huì)考慮的一些解決思路和手段結(jié)尾如何有效的準(zhǔn)備面試中并發(fā)類問題,我已經(jīng)給出我的理解。 showImg(https://segmentfault.com/img/bV7Viy?w=550&h=405); 主題 又到面試季了,從群里,看到許多同學(xué)分享了自己的面試題目,我也抽空在網(wǎng)上搜索了一些許多公司使用的面試題,目前校招和社招的面試題基本都集中在幾個(gè)大方向上...

    xzavier 評論0 收藏0
  • 三年Java后端面試經(jīng)歷

    摘要:前言三年后端開發(fā)經(jīng)驗(yàn),面的目標(biāo)崗位是的高級(jí)后端開發(fā)。面試結(jié)束,應(yīng)該沒有后續(xù)。 前言 三年Java后端開發(fā)經(jīng)驗(yàn),面的目標(biāo)崗位是20k-35k的高級(jí)后端Java開發(fā)。 第一場,基本裸面,關(guān)于曾經(jīng)的項(xiàng)目部門答的不好,所以還是得好好準(zhǔn)備。 某C輪在線旅游公司 筆試 先做半個(gè)小時(shí)的筆試題,一共六個(gè)題目,兩道go語言的基礎(chǔ)題,一道斐波那契相關(guān),一道數(shù)據(jù)庫行列轉(zhuǎn)置,一道實(shí)現(xiàn)一個(gè)棧,還有一道是百萬計(jì)...

    darry 評論0 收藏0
  • 《Java并發(fā)程序設(shè)計(jì)》讀書筆記 第二章 并行程序基礎(chǔ)

    showImg(https://segmentfault.com/img/bVbeIYP?w=4533&h=2073);

    elva 評論0 收藏0
  • 架構(gòu) - 收藏集 - 掘金

    摘要:淺談秒殺系統(tǒng)架構(gòu)設(shè)計(jì)后端掘金秒殺是電子商務(wù)網(wǎng)站常見的一種營銷手段。這兩個(gè)項(xiàng)目白話網(wǎng)站架構(gòu)演進(jìn)后端掘金這是白話系列的文章。 淺談秒殺系統(tǒng)架構(gòu)設(shè)計(jì) - 后端 - 掘金秒殺是電子商務(wù)網(wǎng)站常見的一種營銷手段。 不要整個(gè)系統(tǒng)宕機(jī)。 即使系統(tǒng)故障,也不要將錯(cuò)誤數(shù)據(jù)展示出來。 盡量保持公平公正。 實(shí)現(xiàn)效果 秒殺開始前,搶購按鈕為活動(dòng)未開始。 秒殺開始時(shí),搶購按鈕可以點(diǎn)擊下單。 秒殺結(jié)束后,按鈕按鈕變...

    Riddler 評論0 收藏0
  • 從小白程序員一路晉升為大廠級(jí)技術(shù)專家我看過哪些書籍?(建議收藏)

    摘要:大家好,我是冰河有句話叫做投資啥都不如投資自己的回報(bào)率高。馬上就十一國慶假期了,給小伙伴們分享下,從小白程序員到大廠高級(jí)技術(shù)專家我看過哪些技術(shù)類書籍。 大家好,我是...

    sf_wangchong 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<