AQS及Java中的多種鎖機(jī)制

并發(fā)編程的優(yōu)點(diǎn)和缺點(diǎn)

優(yōu)點(diǎn):

提升性能

將多核CPU的計(jì)算能力發(fā)揮到機(jī)制,性能得到提升

業(yè)務(wù)適用

并行計(jì)算會(huì)比串行計(jì)算更適應(yīng)業(yè)務(wù)需求纷捞,而并發(fā)編程更適用于該模型

缺點(diǎn):

頻繁上線文切換

前提是:線程數(shù)量 > CPU核數(shù)

線程安全

數(shù)據(jù)的可見性、原子性、有序性

無(wú)鎖并發(fā)編程

Runnable和Callable的區(qū)別

  • Runnable從JDK1.1就有了,而Callble是JDK1.5之后增加的
  • Callable執(zhí)行方法是call()萤皂,Runnable執(zhí)行方法是run()
  • Callable任務(wù)執(zhí)行后可返回值,而Runnable任務(wù)是不能返回值(void)
  • call方法可以拋出異常匣椰,run方法不可以
  • 運(yùn)行Callable任務(wù)可拿到Future對(duì)象裆熙,即異步計(jì)算結(jié)果:
    • 它提供了檢查計(jì)算是否完成的方法,以等待計(jì)算的完成禽笑,并檢索計(jì)算的結(jié)果
    • 通過(guò)Future對(duì)象可以了解任務(wù)執(zhí)行情況入录,可取消任務(wù)的執(zhí)行,還可以獲取執(zhí)行結(jié)果
  • 使用線程池方式運(yùn)行:
  • Runnable用ExecutorService的execute方法
  • Callable使用submit方法

為什么使用鎖

并發(fā)操作同一資源

資源競(jìng)爭(zhēng)佳镜、保護(hù)

資源胡釵

并發(fā)編程的產(chǎn)生

Java中鎖的種類

  • synchronized
    • synchronized關(guān)鍵字僚稿,語(yǔ)義層面的定義及使用
    • 隱式地獲取鎖,將鎖的獲取和釋放固化蟀伸,即先獲取在釋放當(dāng)前鎖
  • Lock(JDK1.5)
  • 對(duì)比:
    • 非阻塞方式獲取鎖
    • 獲取鎖喉可被中斷
    • 獲取鎖設(shè)置超時(shí)機(jī)制

synchronize的用法

synchronize的用法.png

Java對(duì)象的組成

Java對(duì)象的組成.png

synchronize 鎖升級(jí)

偏向鎖即有一個(gè)線程A訪問(wèn)帶有鎖的同步代碼塊時(shí)蚀同,會(huì)在對(duì)象頭的mark word記錄線程的ID缅刽,當(dāng)有線程訪問(wèn)同步塊時(shí),會(huì)比較mark word中的線程id蠢络,和當(dāng)前線程是否一致衰猛,如果還是線程A則直接獲取到鎖,當(dāng)有一個(gè)B訪問(wèn)時(shí)刹孔,會(huì)嘗試CAS將對(duì)象頭中的mark wor替換為指向鎖記錄指針啡省,如果替換成功,則當(dāng)前線程獲得到鎖髓霞,如果替換失敗則說(shuō)明有其他線程在競(jìng)爭(zhēng)鎖卦睹,當(dāng)前線程使用自旋來(lái)獲取鎖,當(dāng)自旋次數(shù)達(dá)到一定次數(shù)時(shí)方库,就會(huì)升級(jí)成為重量級(jí)鎖分预,由操作系統(tǒng)Mutexlock實(shí)現(xiàn)線程間的切換

輕量鎖就是偏向鎖+自旋

? 自旋的次數(shù)可以用個(gè)jvm參數(shù)設(shè)置,但是在jdk1.7之后薪捍,就不建議修改這個(gè)參數(shù)了,因?yàn)槌绦蜻M(jìn)行了優(yōu)化配喳,鎖自動(dòng)優(yōu)化酪穿,鎖彈性升級(jí),它自動(dòng)判斷大部分情況需要多少自旋次數(shù)能解決問(wèn)題晴裹,不斷升級(jí)自旋次數(shù)

synchronize 鎖升級(jí).png

Lock

  • Lock概念
    • 是一個(gè)接口被济,它定義和規(guī)范了"獲取和釋放鎖",Lock接口的實(shí)現(xiàn)基本都是通過(guò)聚合了一個(gè)同步器的子類來(lái)完成線程訪問(wèn)控制的
  • Lock接口方法
    • lock() 獲取鎖
    • lockInterruptibly() 可中斷地進(jìn)行獲取鎖
    • tryLock() 非阻塞地嘗試獲取鎖
    • tryLock(long time, TimeUnit unit) 有超時(shí)時(shí)間獲取鎖
    • unlock() 釋放鎖
    • Condition newCondition獲取等待通知組件涧团,該組件與當(dāng)前鎖綁定只磷,只有當(dāng)前線程獲取鎖才可以使用組件await(),await()調(diào)用后釋放鎖
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class TestLock implements Lock {
    // 獲取鎖
    public void lock() {

    }

    // 可中斷地進(jìn)行獲取鎖
    public void lockInterruptibly() throws InterruptedException {

    }

    // 非阻塞地嘗試獲取鎖
    public boolean tryLock() {
        return false;
    }

    // 有超時(shí)時(shí)間獲取鎖
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    // 釋放鎖
    public void unlock() {

    }

    // 獲取等待通知組件泌绣,該組件與當(dāng)前鎖綁定钮追,只有當(dāng)前線程獲取鎖才可以使用組件await(),await()調(diào)用后釋放鎖
    public Condition newCondition() {
        return null;
    }
}

Condition

  • **await(): **造成當(dāng)前線程在接收到信號(hào)或被中斷之前一直處于等待狀態(tài)阿迈。

  • **await(long time, TimeUnit unit): **造成當(dāng)前線程在接收到信號(hào)元媚、被中斷或達(dá)到指定等待時(shí)間之前一直處于等待狀態(tài)。

  • **long awaitNanos(long nanosTimeout): **造成當(dāng)前線程在接收到信號(hào)苗沧、被中斷或達(dá)到指定等待時(shí)間之前一直處于等待狀態(tài)刊棕。返回值表示剩余時(shí)間,如果在nanosTimeout之前喚醒待逞,那么返回值= nanosTimeout - 消耗時(shí)間甥角,如果返回值 <= 0,則可以認(rèn)定它已經(jīng)超時(shí)了识樱。

  • **awaitUninterruptibly(): **造成當(dāng)前線程在接收到信號(hào)之前一直處于等待狀態(tài)嗤无。

    • 注意:該方法對(duì)中斷不敏感震束。
  • **boolean awaitUntil(Date deadline): **造成當(dāng)前線程在接收到信號(hào)、被中斷或到達(dá)指定最后期限之前一直處于等待狀態(tài)翁巍。如果沒有到指定時(shí)間就被通知驴一,則返回true,否則表示到了指定時(shí)間灶壶,返回false肝断。

  • **void signal(): **喚醒一個(gè)等待線程。該線程從等待放放返回前必須獲得與Condition相關(guān)的鎖驰凛。

  • **void signalAll(): **喚醒所有等待線程胸懈,能夠從等待放放返回的線程,必須獲取與Condition相關(guān)的鎖

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class TestCondition {

    public static void main(String[] args) {

        final ReentrantLock reentrantLock = new ReentrantLock();
        final Condition condition = reentrantLock.newCondition();

        new Thread(() -> {

            reentrantLock.lock();

            System.out.println(Thread.currentThread().getName() + "拿到了鎖");
            System.out.println(Thread.currentThread().getName() + "等待信號(hào)");

            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + "拿到信號(hào)");

            reentrantLock.unlock();
        }, "線程1").start();

        new Thread(() -> {

            reentrantLock.lock();

            System.out.println(Thread.currentThread().getName() + "拿到了鎖");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + "發(fā)出信號(hào)");
            condition.signalAll();

            reentrantLock.unlock();

        }, "線程2").start();

    }

}

AQS 隊(duì)列同步器

  • AQS:AbstractQueuedSynchronizer 隊(duì)列同步器
  • 用來(lái)構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架
  • 用int成員變量表示同步狀態(tài)
  • 通過(guò)內(nèi)置的FIFO(先進(jìn)先出)隊(duì)列來(lái)完成資源獲取線程的排隊(duì)工作
  • 大部分同步需求的基礎(chǔ)
  • 同步狀態(tài):
    • getState()
    • setSate(int newState)
    • compareAndSetState(int expect, int update)
  • 鎖是面向使用者的恰响,它定義了使用者與鎖交互的接口(比如可以允許兩個(gè)線程并行訪問(wèn))趣钱,隱藏了實(shí)現(xiàn)細(xì)節(jié)
  • 同步器(AQS)面向的是鎖的創(chuàng)造者OR開發(fā)者,它提供了便捷的工具和方法胚宦,將同步狀態(tài)首有,線程排隊(duì),等待及喚醒等底層操作封裝起來(lái)枢劝,讓開發(fā)者使用更便捷
  • 鎖和同步器很多地隔離了使用者和實(shí)現(xiàn)者所需關(guān)注的范圍及各自關(guān)注的邏輯細(xì)節(jié)

volatile

  • 保證不同線程對(duì)統(tǒng)一變量操作時(shí)的可見性
  • 禁止指令重排序

MESI協(xié)議

  • 將當(dāng)前處理器緩存行的數(shù)據(jù)寫會(huì)系統(tǒng)內(nèi)存
  • 這個(gè)寫回內(nèi)存的操作會(huì)使其他CPU里緩存了該內(nèi)存地址的數(shù)據(jù)無(wú)效
  • volatile下的MESI:
    • Lock前綴的指令會(huì)引起處理器緩存寫回內(nèi)存
    • 一個(gè)處理器的緩存回寫到內(nèi)存會(huì)導(dǎo)致其他處理器的緩存失效
    • 當(dāng)處理器發(fā)現(xiàn)本地緩存失效后井联,就會(huì)從內(nèi)存中重讀該變量數(shù)據(jù),既可以獲取當(dāng)前最新值
MESI協(xié)議.png

AQS實(shí)現(xiàn)原理

  • 同步器依賴內(nèi)部的隊(duì)列完成同步狀態(tài)的管理您旁,當(dāng)線程獲取同步狀態(tài)失敗時(shí)烙常,同步器會(huì)將當(dāng)前線程與等待狀態(tài)等信息構(gòu)造成為一個(gè)節(jié)點(diǎn)(Node)將其放入同步隊(duì)列,同時(shí)會(huì)阻塞當(dāng)前線程鹤盒。同步狀態(tài)釋放時(shí)蚕脏,喚醒首節(jié)點(diǎn)中的線程,使其再次嘗試獲取同步狀態(tài)侦锯,
  • 主要包括:
    • 同步隊(duì)列
    • 獨(dú)占式同步狀態(tài)獲取與釋放
    • 共享式同步狀態(tài)獲取與釋放
    • 超時(shí)獲取同步狀態(tài)等同步器的核心數(shù)據(jù)結(jié)構(gòu)與模板方法

同步隊(duì)列

  • 同步隊(duì)列中的節(jié)點(diǎn)(Node):

    • 保存獲取同步狀態(tài)失敗線程的引用驼鞭,等待狀態(tài)以及前置和后置節(jié)點(diǎn)
  • 包含的字段:

    • prev 前置節(jié)點(diǎn)
    • next 后置節(jié)點(diǎn)
    • thread 獲取同步狀態(tài)的線程
    • waitStatus 等待狀態(tài)
    • nextWaiter 等待隊(duì)列中的后置節(jié)點(diǎn)
    AQS同步隊(duì)列.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市尺碰,隨后出現(xiàn)的幾起案子终议,更是在濱河造成了極大的恐慌,老刑警劉巖葱蝗,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件穴张,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡两曼,警方通過(guò)查閱死者的電腦和手機(jī)皂甘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)悼凑,“玉大人偿枕,你說(shuō)我怎么就攤上這事璧瞬。” “怎么了渐夸?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵嗤锉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我墓塌,道長(zhǎng)瘟忱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任苫幢,我火速辦了婚禮访诱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘韩肝。我一直安慰自己触菜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布哀峻。 她就那樣靜靜地躺著涡相,像睡著了一般。 火紅的嫁衣襯著肌膚如雪剩蟀。 梳的紋絲不亂的頭發(fā)上漾峡,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音喻旷,去河邊找鬼。 笑死牢屋,一個(gè)胖子當(dāng)著我的面吹牛且预,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播烙无,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼锋谐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了截酷?” 一聲冷哼從身側(cè)響起涮拗,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎迂苛,沒想到半個(gè)月后三热,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡三幻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年就漾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片念搬。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡抑堡,死狀恐怖摆出,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情首妖,我是刑警寧澤偎漫,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站有缆,受9級(jí)特大地震影響象踊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜妒貌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一通危、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧灌曙,春花似錦菊碟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蚣驼,卻和暖如春魄幕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背颖杏。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工纯陨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人留储。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓翼抠,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親获讳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子阴颖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350