并發(fā)編程

并發(fā)編程基礎(chǔ)

Java線程模型

發(fā)生了系統(tǒng)調(diào)用的鎖患久,就是重量鎖

MMU: 虛擬地址映射

線程類型

用戶線程:使用Java代碼創(chuàng)建的線程贡避。

內(nèi)核線程:操作系統(tǒng)對(duì)應(yīng)的線程

對(duì)應(yīng)關(guān)系

一對(duì)一

一個(gè)用戶線程對(duì)應(yīng)一個(gè)內(nèi)核線程

優(yōu)點(diǎn):簡(jiǎn)單庵寞,幾乎所有對(duì)線程的操作都交給了內(nèi)核線程北苟。

缺點(diǎn):

  1. 對(duì)用戶線程的大部分操作會(huì)映射到內(nèi)核線程上,引起用戶態(tài)和內(nèi)核態(tài)的頻繁切換怕篷。

  2. 創(chuàng)建大量線程對(duì)系統(tǒng)性能有影響历筝。

多對(duì)一

多個(gè)用戶線程對(duì)應(yīng)一個(gè)內(nèi)核線程

優(yōu)點(diǎn):用戶線程的很多操作對(duì)內(nèi)核來說是透明的,不需要進(jìn)行頻繁的用戶態(tài)和內(nèi)核態(tài)的切換廊谓,線程的創(chuàng)建梳猪、調(diào)度、同步非痴舯裕快春弥。

缺點(diǎn):

  1. 其中一個(gè)線程阻塞,其他用戶線程也無法執(zhí)行叠荠。

  2. 內(nèi)核不知道用戶有哪些線程匿沛,無法像內(nèi)核線程一樣實(shí)現(xiàn)完整的調(diào)度、優(yōu)先級(jí)等榛鼎。

多對(duì)多

用戶態(tài)和內(nèi)核態(tài)

linux系統(tǒng)虛擬地址映射

物理地址空間 :物理地址就是真實(shí)地址逃呼,對(duì)應(yīng)真實(shí)的內(nèi)存條。內(nèi)存像一個(gè)數(shù)組借帘,每個(gè)存儲(chǔ)單元被分配了一個(gè)地址蜘渣,就是物理地址。

虛擬地址空間 : 每個(gè)進(jìn)程擁有一個(gè)巨大的連續(xù)內(nèi)存空間肺然,甚至比內(nèi)存空間更大,這是一個(gè)“假象”腿准。

CPU使用虛擬地址像內(nèi)存尋址际起,通過內(nèi)存管理單元MMU硬件,把虛擬地址轉(zhuǎn)換成真實(shí)的物理地址吐葱。

CPU有四種不同的執(zhí)行級(jí)別0-3街望,linux用0表示內(nèi)核態(tài),3表示用戶態(tài)弟跑。

切換方式

  • 系統(tǒng)調(diào)用 (關(guān)注重點(diǎn))
  • 異常(不是Java中的異常)
  • 外圍設(shè)備中斷

CPU上下文

CPU寄存器和程序計(jì)數(shù)器就是CPU的上下文灾前,都是CPU在運(yùn)行任務(wù)前,必須的依賴環(huán)境孟辑。

CPU寄存器:CPU內(nèi)置的容量小哎甲、速度極快的內(nèi)存蔫敲。

程序計(jì)數(shù)器:用來存儲(chǔ)CPU正在執(zhí)行的指令位置,或者即將執(zhí)行的下一條指令位置炭玫。

CPU上下文切換

將前一個(gè)任務(wù)的CPU上下文保存奈嘿,然后加載新任務(wù)的上下文到寄存器和程序計(jì)數(shù)器,最后再跳轉(zhuǎn)到程序計(jì)數(shù)器所指的新位置吞加,運(yùn)行新的任務(wù)裙犹。

CPU上下文切換類型

進(jìn)程上下文切換
  1. 系統(tǒng)調(diào)用:從用戶態(tài)到內(nèi)核態(tài)的轉(zhuǎn)變,通過系統(tǒng)調(diào)用來完成衔憨。 屬于同進(jìn)程內(nèi)的CPU上下文切換叶圃。一次系統(tǒng)調(diào)用過程,發(fā)生兩次CPU上下文切換(用戶態(tài) —> 內(nèi)核態(tài) —> 內(nèi)核態(tài))

  2. 進(jìn)程間上下文切換:進(jìn)程的上下文不僅包括了虛擬內(nèi)存践图、棧盗似、全局變量等用戶空間資源,還包括了內(nèi)核堆棧平项、寄存器等內(nèi)核空間資源赫舒。比系統(tǒng)調(diào)用多了一步,在保存內(nèi)核態(tài)資源之前闽瓢,需要把該進(jìn)程的用戶態(tài)資源保存下來接癌,加載了下一進(jìn)程的內(nèi)核態(tài)后,需要刷新新進(jìn)程的虛擬內(nèi)存和用戶棧扣讼。

進(jìn)程上下文切換場(chǎng)景:

  • CPU輪轉(zhuǎn)缺猛,進(jìn)程分配的時(shí)間片耗盡。
  • 系統(tǒng)資源不足
  • 調(diào)用sleep等方法將自己主動(dòng)掛起
  • 有優(yōu)先級(jí)更高的進(jìn)程運(yùn)行
線程上下文切換
  1. 兩個(gè)線程屬于不同進(jìn)程: 資源不共享椭符,切換過程和進(jìn)程上下文切換一樣荔燎。
  2. 屬于同一進(jìn)程:虛擬內(nèi)存共享,只需要切換線程的私有數(shù)據(jù)销钝、寄存器等不共享的數(shù)據(jù)有咨。

并發(fā)相關(guān)知識(shí)

對(duì)象頭

基本對(duì)象布局:對(duì)象頭(96bit)+ [實(shí)例數(shù)據(jù)] + [對(duì)齊填充數(shù)據(jù)]

實(shí)例數(shù)據(jù)就是定義的全局變量所占的內(nèi)存。

對(duì)象的大小必須為8字節(jié)(byte)的整數(shù)倍蒸健。1byte = 8 bit

添加這個(gè)依賴座享,可以打印出對(duì)象的信息ClassLayout.parseClass(A.class).toPrintable();

<dependency> 
  <groupId>org.openjdk.jol</groupId> 
  <artifactId>jol-core</artifactId> 
  <version>0.9</version> 
</dependency>

大小端模式

小端模式:高字節(jié)存在高地址,低字節(jié)存在低地址似忧。所以打印出來是反的渣叛。

如 1的字節(jié)碼為:00000000 00000000 00000000 00000001

打印出來就是:00000001 00000000 00000000 00000000

對(duì)象頭:

mark world 占64bit

對(duì)象頭
mark word存儲(chǔ)數(shù)據(jù)

Klass point 指向?qū)ο蟮脑獢?shù)據(jù)。開啟了指針壓縮盯捌,4位淳衙。沒開啟,就占8位

偏向鎖 01 輕量鎖 00 重量鎖 10 GC 11

偏向鎖:

101 可偏向

001 不可偏向 計(jì)算了hashCode之后就不能偏向了

公平鎖和非公平鎖

總結(jié):

一朝排隊(duì),永遠(yuǎn)排隊(duì)

基于JDK1.8的解釋:

非公平鎖

  1. 首先會(huì)在加鎖的時(shí)候去搶鎖(公平鎖不會(huì)上來就拿鎖)

  2. 如果加鎖失敗箫攀,判斷鎖是否被人持有了肠牲,沒有被人持有的話,會(huì)直接去進(jìn)行加鎖(公平鎖會(huì)判斷是否有人排隊(duì))匠童,成功進(jìn)入代碼塊埂材。失敗則進(jìn)入隊(duì)列

  3. 進(jìn)入隊(duì)列后,如果前面那個(gè)是head,則再次嘗試加鎖汤求,失敗則park俏险,進(jìn)行真正的排隊(duì)。

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     */
    final void lock() {
        // 第1步 加鎖的時(shí)候就去獲取鎖
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    /**
     *acquire(1)方法會(huì)調(diào)用到該方法扬绪,再次嘗試獲取鎖
     *
     */
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

abstract static class Sync extends AbstractQueuedSynchronizer {
  final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
      // 第2步 鎖沒別人持有竖独,會(huì)去再次嘗試拿鎖
      if (compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(current);
        return true;
      }
    }
    // 重入鎖
    else if (current == getExclusiveOwnerThread()) {
      int nextc = c + acquires;
      if (nextc < 0) // overflow
        throw new Error("Maximum lock count exceeded");
      setState(nextc);
      return true;
    }
    return false;
  }
  
  ...
}

公平鎖

  1. 調(diào)用lock的時(shí)候不會(huì)去嘗試加鎖,回去查看隊(duì)列中有沒有排隊(duì)的節(jié)點(diǎn)Node挤牛,如果有則進(jìn)入隊(duì)列(并不等于排隊(duì))莹痢,
  2. 會(huì)再次進(jìn)行查看前面那個(gè)節(jié)點(diǎn)是否為head,如果是墓赴,則再次嘗試拿鎖竞膳。失敗則排隊(duì)park
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // 會(huì)判斷是否有節(jié)點(diǎn)在排隊(duì)
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }


public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
  
      final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
}

是否開啟了偏向模式?涉及到偏向的撤銷诫硕。

偏向延遲的時(shí)間是4秒鐘坦辟。JVM自身的代碼,基本上不存在偏向鎖章办,所以把偏向鎖給禁用掉了锉走,因?yàn)槠虻某蜂N很耗費(fèi)性能。而過了4秒鐘之后藕届,JVM認(rèn)為自己已經(jīng)執(zhí)行完成了挪蹭,而用戶寫的代碼不知道是不是偏向鎖,所以開啟偏向鎖休偶。自己可以在啟動(dòng)的時(shí)候設(shè)置JVM的參數(shù)梁厉,設(shè)置啟動(dòng)延遲為0秒。-XX:BiasedLockingStartupDelay=0 -XX:+UseBiasedLocking //啟用偏向鎖

輕量鎖00加鎖成功一定是要無鎖的狀態(tài)

pthread_mutex_lock 重量鎖

JMH 基準(zhǔn)測(cè)試 java性能測(cè)試

public class A {
    static boolean  isRunning = true;
    static int i;

     void  test(){
        log.info("t1.start-----");
        while (isRunning){
            // 代碼中不做什么事情
            i++;
            i = i + 3;
        }
        log.error("t1.end-----");

    }

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

        A a = new A();
        new Thread(a::test,"t1").start();

        Thread.sleep(200);
        // 睡眠過后椅贱,更改這個(gè)值懂算,不會(huì)讓t1線程停止運(yùn)行。isRunning改成volite可以實(shí)現(xiàn)庇麦,不是因?yàn)椴豢梢娦裕且驗(yàn)榻沽酥噶钪嘏?        // 其實(shí)是Jvm對(duì)代碼進(jìn)行了優(yōu)化喜德,發(fā)現(xiàn)while循環(huán)中并沒有做什么操作山橄,創(chuàng)建了一個(gè)臨時(shí)變量等于isRunning,來控制了循環(huán)
        isRunning = false;
    }
}

happend before JVM對(duì)代碼進(jìn)行了優(yōu)化舍悯,如果發(fā)現(xiàn)

兩條CAS指令不能保證原子性

synchronize發(fā)生異常航棱,如果沒有進(jìn)行try catch 會(huì)釋放鎖睡雇。 monitorExit指令

Lock特點(diǎn)

  1. 可打斷(lock.lockInterruptibly() 獲取鎖),可重入
  2. 可以設(shè)置超時(shí)時(shí)間
  3. 可以設(shè)置為公平鎖
  4. 支持多個(gè)條件變量
  5. 支持讀寫鎖

打斷之后是在異常中處理的饮醇,自己可以在打斷的異常中做自己的邏輯處理它抱。

線程的順序執(zhí)行:

  1. wait和notify實(shí)現(xiàn)(while循環(huán))
  2. LockSupport.park 和 LockSupport.unpark(t1)
  3. Join(),future()

并發(fā)編程基礎(chǔ)知識(shí)

高并發(fā)的好處

  1. 充分利用CPU資源
  2. 加快響應(yīng)用戶時(shí)間
  3. 代碼模塊化朴艰,異步化观蓄,簡(jiǎn)單化

多線程注意事項(xiàng)

  1. 線程之間的安全性
  2. 線程之間的死鎖
  3. 線程對(duì)服務(wù)器資源的消耗

線程的啟動(dòng)與中止

啟動(dòng):

  1. 繼承Thread類,調(diào)用start方法
  2. 實(shí)現(xiàn)Runnable祠墅,交給Thread運(yùn)行
  3. 實(shí)現(xiàn)Callable侮穿,通過FutureTask把Callable包裝成Runnable,交給Thread運(yùn)行毁嗦,可以通過FutureTask拿到Callable運(yùn)行后的返回值亲茅。

中止:

  1. run方法執(zhí)行完成,或者拋出一個(gè)未處理的異常導(dǎo)致線程提前結(jié)束
  2. 調(diào)用suspend()狗准、resume()stop()這些過期方法克锣,不建議使用,調(diào)用后不會(huì)釋放占有的資源

建議使用中斷式的操作腔长,調(diào)用線程的interrupt()方法對(duì)其進(jìn)行中斷操作袭祟,這個(gè)是協(xié)作式的。該線程通過isInterrupted()方法進(jìn)行判斷是否被中斷饼酿。調(diào)用靜態(tài)方法Thread.interrupted()會(huì)將中斷標(biāo)識(shí)改為false榕酒,也可以作為判斷。

處于死鎖的線程無法被中斷

線程

線程中的方法

run()方法故俐,本質(zhì)是就是一個(gè)成員方法想鹰, 可重復(fù)執(zhí)行,也可被單獨(dú)調(diào)用

start()方法药版,只有執(zhí)行了start()方法辑舷,才真正啟動(dòng)線程,只能執(zhí)行一次槽片。

yield()方法何缓,讓出CPU的執(zhí)行權(quán),但是讓出時(shí)間不可設(shè)定还栓,也不會(huì)釋放鎖資源碌廓。

join()方法,把指定線程A加入到當(dāng)前線程剩盒,知道A線程執(zhí)行完成后谷婆,才會(huì)繼續(xù)執(zhí)行當(dāng)前線程。兩個(gè)交替的線程可以合并為順序執(zhí)行。

sleep()方法纪挎,調(diào)用后期贫,當(dāng)前線程會(huì)休眠,不會(huì)釋放鎖异袄。

setPriority()方法通砍,設(shè)置線程的優(yōu)先級(jí),高優(yōu)先級(jí)的線程分配的時(shí)間片數(shù)量要多于低優(yōu)先級(jí)的線程烤蜕。默認(rèn)優(yōu)先級(jí)為5封孙,范圍為1-10。高優(yōu)先級(jí)的線程并不能保證一定會(huì)比低優(yōu)先級(jí)的線程先執(zhí)行玖绿。因?yàn)榫€程的調(diào)度最終取決于操作系統(tǒng)敛瓷。

守護(hù)線程

Daemon守護(hù)線程是一種支持型線程,主要用作程序中后臺(tái)調(diào)度和支持性工作斑匪。當(dāng)Java虛擬機(jī)中不存在非Daemon線程的時(shí)候呐籽,Java虛擬機(jī)就會(huì)退出。使用Thread.setDaemon(true)將線程設(shè)置為守護(hù)線程蚀瘸。虛擬機(jī)退出時(shí)守護(hù)線程的finally塊不一定會(huì)執(zhí)行狡蝶,所以守護(hù)線程不能依靠finally塊來確保執(zhí)行關(guān)閉或清理資源。垃圾回收線程就是守護(hù)線程贮勃。

等待通知機(jī)制

線程A調(diào)用了對(duì)象o的wait()方法進(jìn)入等待狀態(tài)贪惹,而線程B調(diào)用了對(duì)象o的notify()或者notifyAll()方法,線程A收到通知后寂嘉,從o.wait()方法返回奏瞬,執(zhí)行后續(xù)操作。這幾個(gè)方法都是針對(duì)對(duì)象的泉孩,都是Object中的方法硼端。

等待通知范式:

// 等待方遵循的范式
synchronized(對(duì)象){// 1.獲取對(duì)象的鎖
  while(條件不滿足){// 2.條件不滿足,調(diào)用對(duì)象的wait方法寓搬,被通知后仍要檢查條件
    對(duì)象.wait();// 會(huì)在這進(jìn)行阻塞
  }
  對(duì)應(yīng)的邏輯處理 // 3.條件滿足則執(zhí)行對(duì)應(yīng)的邏輯
}

// 通知方范式
synchronized(對(duì)象){// 1.獲得對(duì)象的鎖
  改變條件 // 2.改變條件
  對(duì)象.notifyAll(); // 3.通知所有等待在線程上的線程
}
  

wait()珍昨、notify()、notifyAll()方法句喷,線程必須要獲得該對(duì)象的鎖镣典,只能在同步方法或者同步代碼塊中調(diào)用。進(jìn)入wait方法后唾琼,當(dāng)前線程就會(huì)釋放鎖兄春。調(diào)用了notifyAll()后其它線程就會(huì)去競(jìng)爭(zhēng)鎖。但是鎖釋放是在同步代碼塊執(zhí)行完成后釋放锡溯,所以一般notifyAll方法都是在同步代碼塊的最后一行執(zhí)行神郊。

線程的并發(fā)工具類

Fork-Join

分而治之肴裙,設(shè)計(jì)思想是將一個(gè)難以解決的大問題趾唱,分割成一些規(guī)模較小的相同問題涌乳,各個(gè)擊破,分而治之甜癞,最后進(jìn)行join匯總夕晓。快速排序悠咱,歸并排序蒸辆,二分查找,大數(shù)據(jù)中M/R都是分而治之的思想析既。

CountDownLatch

閉鎖躬贡,能夠使一個(gè)線程等待其它線程完成各自的工作后再執(zhí)行。通過一個(gè)計(jì)數(shù)器來實(shí)現(xiàn)眼坏,計(jì)數(shù)器的初始值為初十任務(wù)的數(shù)量拂玻,每完成一個(gè)任務(wù)后,計(jì)數(shù)器減1宰译,當(dāng)計(jì)數(shù)器到達(dá)0時(shí)檐蚜,表示所以任務(wù)已完成。在閉鎖上等待 CountDownLatch.await()方法的線程就可以恢復(fù)執(zhí)行任務(wù)沿侈。

static CountDownLatch countDownLatch = new CountDownLatch(5);
public static void countDownLatchTest() {
    for (int i = 0; i < 5; i++) {
        int finalI = i;
        new Thread(() -> {
            System.out.println("子線程運(yùn)行... " + finalI);
            countDownLatch.countDown();// 計(jì)數(shù)器減1
        }).start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public static void main(String[] args) {
   countDownLatchTest();
   try {
       countDownLatch.await();// 等計(jì)數(shù)器為0之后闯第,停止阻塞,恢復(fù)執(zhí)行
   } catch (InterruptedException e) {
       e.printStackTrace();
   }
   System.out.println("主線程運(yùn)行完成...");
}

并非說任務(wù)數(shù)量和線程數(shù)量一點(diǎn)要相等缀拭,一個(gè)線程可以多吃調(diào)用countDownLatch.countDown();進(jìn)行計(jì)數(shù)器減1

CycilcBarrier

可循環(huán)使用的屏障咳短。讓一組線程到達(dá)一個(gè)屏障時(shí)被阻塞,直到最后一個(gè)線程到達(dá)屏障時(shí)蛛淋,屏障才會(huì)開放咙好,所有被屏蔽攔截的線程才會(huì)繼續(xù)執(zhí)行。

CyclicBarrier 默認(rèn)的構(gòu)造方法是 CyclicBarrier(int parties)铣鹏,其參數(shù)表示屏障攔截的線程數(shù)量敷扫,每個(gè)線程調(diào)用 CyclicBarrier.await 方法告訴 CyclicBarrier 我已經(jīng)到達(dá)了屏障,然 后當(dāng)前線程被阻塞诚卸。

CyclicBarrier 還提供一個(gè)更高級(jí)的構(gòu)造函數(shù) CyclicBarrie(r int parties葵第,Runnable barrierAction),用于在線程到達(dá)屏障時(shí)合溺,優(yōu)先執(zhí)行 barrierAction卒密,方便處理更復(fù) 雜的業(yè)務(wù)場(chǎng)景。

Semaphore

信號(hào)量用來控制同時(shí)訪問特定資源的線程數(shù)量棠赛∠妫可以用于做流量控制膛腐。Semaphore 的構(gòu)造方法 Semaphore(int permits)接受一個(gè)整型的數(shù)字, 表示可用的許可證數(shù)量鼎俘。線程使用 Semaphore的 acquire()方法獲取一個(gè)許可證哲身,使用完之后調(diào)用 release()方法歸還許可證。還可以用 tryAcquire()方法嘗試獲取許可證贸伐。

Semaphore 還提供一些其他方法勘天,具體如下。

  • intavailablePermits():返回此信號(hào)量中當(dāng)前可用的許可證數(shù)捉邢。

  • intgetQueueLength():返回正在等待獲取許可證的線程數(shù)脯丝。

  • booleanhasQueuedThreads():是否有線程正在等待獲取許可證。

  • void reducePermits(int reduction):減少 reduction 個(gè)許可證伏伐,是個(gè)protected 方法宠进。

  • Collection getQueuedThreads():返回所有等待獲取許可證的線程集合,是個(gè)protected 方法藐翎。

Exchange

Exchanger交換者是一個(gè)用于線程間協(xié)作的工具類材蹬。用于進(jìn)行線程間數(shù)據(jù)交換。提供一個(gè)同步點(diǎn)阱高,在這個(gè)同步點(diǎn)赚导,兩個(gè)線程可以交換彼此的數(shù)據(jù)。通過exchange()方法交換數(shù)據(jù)赤惊,第一個(gè)線程先執(zhí)行exchange方法吼旧,會(huì)等待第二個(gè)線程執(zhí)行exchange方法,兩個(gè)線程都到達(dá)同步點(diǎn)時(shí)未舟,就可以交換數(shù)據(jù)圈暗。

原子操作CAS

如果有兩個(gè)操作A和B,如果從執(zhí)行 A 的線程來看裕膀,當(dāng)另一個(gè)線程執(zhí)行 B 時(shí)员串,要么將 B 全部執(zhí)行完,要么完全不執(zhí)行 B昼扛,那么 A 和 B 對(duì)彼此來說是原子的寸齐。

鎖實(shí)現(xiàn)原子操作存在的問題:被阻塞的線程優(yōu)先級(jí)很高,獲得鎖的線程一直不釋放鎖抄谐,大量線程競(jìng)爭(zhēng)支援渺鹦,導(dǎo)致CPU資源浪費(fèi),死鎖問題蛹含。

CAS基本思路:內(nèi)存地址V上的值和期望的值A(chǔ)相等毅厚,則給其賦予新值B,否則不做任何事浦箱,只返回原值吸耿。

CAS實(shí)現(xiàn)原子操作的3大問題

  1. ABA問題

    一個(gè)值原來是A祠锣,后面變成了B,又修改成了A咽安,使用CAS進(jìn)行檢查時(shí)發(fā)現(xiàn)其值沒有發(fā)生變化伴网,但是實(shí)際上是已經(jīng)變化過了的。

    解決思路就是使用版本號(hào)板乙,在變量面前加上版本號(hào)是偷。

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

    自旋CAS如果長時(shí)間不成功,會(huì)給CPU帶來非常大的執(zhí)行開銷募逞。

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

Jdk中原子相關(guān)操作類

  1. AtomicInteger
  2. AtomicIntegerArray
  3. AtomicReference
  4. AtomicStampedRefrence 利用版本戳形式記錄了每次改變的版本號(hào),避免ABA問題
  5. AtomicMarkableReference 帶有標(biāo)記位的引用類型
  6. AtomicIntegerFieldUpdater
  7. AtomicLongFieldUpdater
  8. AtomicReferenceFieldUpdater

顯示鎖

synchronized關(guān)鍵字會(huì)隱式的獲取鎖馋评,它將鎖的獲取和釋放固化了放接,就是先獲取再釋放。

顯示鎖提供了隱式鎖很多沒有的功能留特,比如嘗試非阻塞的獲取鎖纠脾,能被中斷的獲取鎖,超時(shí)獲取鎖等蜕青。

顯示鎖的特性

Lock的標(biāo)準(zhǔn)用法

lock.lock();// 獲取鎖
try{
  ...
}finally{
  lock.unlock();// 釋放鎖
}

在finally語句塊中執(zhí)行unlock()方法苟蹈,保證最終能夠釋放鎖。

Lock的常用API

Lock常用api

公平和非公平鎖

先對(duì)鎖獲取的請(qǐng)求一定先被滿足右核,那么這個(gè)鎖就是公平鎖慧脱。也就是等待時(shí)間最長的線程優(yōu)先獲取鎖。反之就是非公平鎖贺喝。非公平鎖效率比公平鎖高菱鸥。

恢復(fù)一個(gè)被掛起的線程與該線程真正開始執(zhí)行之間存在嚴(yán)重的延遲,有可能在這個(gè)延遲時(shí)間段內(nèi)有另外一個(gè)線程獲取到鎖并且執(zhí)行完成躏鱼,被掛起的線程還沒有被喚醒氮采。正因?yàn)檫@個(gè)情況,非公平鎖比公平鎖效率更高染苛。

ReentrantLock可重入鎖

定義:同一個(gè)線程對(duì)于已經(jīng)獲得到的鎖鹊漠,可以多次繼續(xù)申請(qǐng)到該鎖的使用權(quán)。Synchronized隱式的支持可重入茶行。ReentrantLock實(shí)現(xiàn)Lock接口和序列化接口躯概,構(gòu)造函數(shù)傳入ture表示使用公平鎖。默認(rèn)為非公平鎖拢军。

讀寫鎖ReentrantReadWriteLock

排它鎖:在同一時(shí)刻楞陷,只允許一個(gè)線程進(jìn)行訪問。

讀寫鎖在同一個(gè)時(shí)刻可以允許多個(gè)讀線程訪問茉唉。但是寫線程訪問時(shí)固蛾,所有的讀線程和其它線程均被阻塞结执。

維護(hù)了一個(gè)讀鎖和一個(gè)寫鎖,通過分離讀鎖和寫鎖艾凯,提升并發(fā)性献幔。

Condition接口

Condition 接口提供 了類似 Object 的監(jiān)視器方法,與 Lock 配合可以實(shí)現(xiàn)等待/通知模式趾诗。

Condition常用方法

使用范式:

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

public void conditionWaiter() throws  InterruptedException{
    lock.lock();
    try {
        condition.await();
    } finally {
        lock.unlock();
    }
}

public void conditionSignal() {
    lock.lock();
    try {
        condition.signal();
    }finally {
        lock.unlock();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蜡感,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子恃泪,更是在濱河造成了極大的恐慌郑兴,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贝乎,死亡現(xiàn)場(chǎng)離奇詭異情连,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)览效,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門却舀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锤灿,你說我怎么就攤上這事挽拔。” “怎么了但校?”我有些...
    開封第一講書人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵螃诅,是天一觀的道長。 經(jīng)常有香客問我始腾,道長州刽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任浪箭,我火速辦了婚禮穗椅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘奶栖。我一直安慰自己匹表,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開白布宣鄙。 她就那樣靜靜地躺著袍镀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪冻晤。 梳的紋絲不亂的頭發(fā)上递览,一...
    開封第一講書人閱讀 52,196評(píng)論 1 308
  • 那天定拟,我揣著相機(jī)與錄音砌些,去河邊找鬼。 笑死锦茁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的叉存。 我是一名探鬼主播码俩,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼歼捏!你這毒婦竟也來了稿存?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤瞳秽,失蹤者是張志新(化名)和其女友劉穎瓣履,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寂诱,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拂苹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了痰洒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浴韭,死狀恐怖丘喻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情念颈,我是刑警寧澤泉粉,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站榴芳,受9級(jí)特大地震影響嗡靡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜窟感,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一讨彼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧柿祈,春花似錦哈误、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至卢佣,卻和暖如春重荠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背虚茶。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來泰國打工戈鲁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留仇参,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓荞彼,卻偏偏與公主長得像冈敛,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鸣皂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 最近自己在整理關(guān)于并發(fā)編程相關(guān)的知識(shí)點(diǎn)抓谴,要細(xì)致的了解每個(gè)知識(shí)背后產(chǎn)生的原因和相關(guān)處理并發(fā)的底層原理,確實(shí)還...
    Android開發(fā)_Hua閱讀 130評(píng)論 0 1
  • 鎖 鎖分為 類鎖 對(duì)象鎖 顯示鎖image.png寫在函數(shù)上的鎖寞缝,不用去設(shè)置鎖的誰癌压,會(huì)自動(dòng)去尋找一把鎖,并且如果是...
    星宇V閱讀 860評(píng)論 0 3
  • 1荆陆、什么是線程安全滩届,怎么保證線程安全? 線程安全可以簡(jiǎn)單理解為一個(gè)方法或者一個(gè)實(shí)例可以在多線程環(huán)境中使用而不會(huì)出現(xiàn)...
    技術(shù)滅霸閱讀 174評(píng)論 0 0
  • 1.并發(fā)編程三要素 原子性 原子被啼,即一個(gè)不可再被分割的顆粒帜消。在Java中原子性指的是一個(gè)或多個(gè)操作要么全部執(zhí)行成功...
    huang_liang_a閱讀 1,101評(píng)論 0 0
  • 本文是我自己在秋招復(fù)習(xí)時(shí)的讀書筆記,整理的知識(shí)點(diǎn)浓体,也是為了防止忘記泡挺,尊重勞動(dòng)成果,轉(zhuǎn)載注明出處哦命浴!如果你也喜歡娄猫,那...
    波波波先森閱讀 11,273評(píng)論 4 56