JDK內(nèi)置鎖深入探究

一浪蹂、序言

本文講述僅針對(duì) JVM 層次的內(nèi)置鎖,不涉及分布式鎖告材。

鎖有多種分類形式坤次,比如公平鎖與非公平鎖、可重入鎖與非重入鎖斥赋、獨(dú)享鎖與共享鎖缰猴、樂(lè)觀鎖與悲觀鎖、互斥鎖與讀寫(xiě)鎖灿渴、自旋鎖洛波、分段鎖和偏向鎖/輕量級(jí)鎖/重量級(jí)鎖。

下面將配合示例講解各種鎖的概念骚露,期望能夠達(dá)到如下目標(biāo):一是在生產(chǎn)環(huán)境中不錯(cuò)誤的使用鎖蹬挤;二是在生產(chǎn)環(huán)境中選擇恰當(dāng)?shù)逆i。

對(duì)鎖了解不多的情況下棘幸,應(yīng)該首先保證業(yè)務(wù)的正確性焰扳,然后考慮性能,比如萬(wàn)金油synchronized鎖或者自帶多重屬性的ReentrantReadWriteLock鎖误续。不因并發(fā)導(dǎo)致業(yè)務(wù)錯(cuò)誤吨悍,不出現(xiàn)死鎖。

隨著對(duì)鎖的了解增多蹋嵌,需要更加精準(zhǔn)的選擇各類鎖以保證更高性能要求育瓜。

二、鎖的分類

Java 中有兩種加鎖的方式:一是 synchronized 關(guān)鍵字栽烂,二是用 Lock 接口的實(shí)現(xiàn)類躏仇。

需要通過(guò)加(互斥)鎖來(lái)解決線程安全問(wèn)題的鎖稱之為悲觀鎖;不通過(guò)加鎖來(lái)解決線程安全問(wèn)題的鎖稱之為樂(lè)觀鎖腺办。

鎖的性能比較:互斥鎖 < 讀寫(xiě)鎖焰手、自旋鎖 < 樂(lè)觀鎖

讀寫(xiě)鎖和自旋鎖分別從兩個(gè)不同角度提升鎖的效率怀喉,前者通過(guò)共享讀鎖來(lái)提高效率书妻;后者通過(guò)回避阻塞-喚醒上下文切換來(lái)提高效率。

(一)公平鎖/非公平鎖

公平鎖和非公平鎖具體實(shí)現(xiàn)類有Semaphore躬拢、ReentrantLockReentrantReadWriteLock躲履。

公平與否是指參與競(jìng)爭(zhēng)的線程是否都有機(jī)會(huì)獲得鎖见间,公平鎖:多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖;非公平鎖并不是按照申請(qǐng)鎖的順序來(lái)獲取鎖崇呵,極端情況下可能會(huì)有線程一直無(wú)法獲取到鎖缤剧。

公平鎖維護(hù)一個(gè)虛擬的先進(jìn)先出隊(duì)列,按照次序排隊(duì)申請(qǐng)獲取鎖域慷。

1荒辕、概念解讀

為何按鎖的申請(qǐng)順序按照先進(jìn)先出的順序獲取鎖能夠保證公平?當(dāng)采用先進(jìn)先出的排隊(duì)機(jī)制時(shí)犹褒,所有處于等待隊(duì)列中的線程理論上都有機(jī)會(huì)獲得鎖抵窒,并且隨著時(shí)間的推移,獲得鎖的機(jī)會(huì)越來(lái)越大叠骑。

不是按照申請(qǐng)鎖的順序來(lái)獲取鎖如何解讀李皇?synchronized 鎖是典型的非公平鎖,表現(xiàn)形式是所有參與獲取鎖的線程是否能夠獲得鎖是不可預(yù)測(cè)的宙枷。

公平鎖的深層次內(nèi)涵是只要線程有獲得鎖的需求掉房,在絕對(duì)的時(shí)間里,一定能夠獲得鎖慰丛。比如服務(wù)器連接資源卓囚,不存在客戶端連接不上的情況,這是公平鎖的典型的應(yīng)用诅病。

2哪亿、鎖代碼層次表示

Semaphore

// 非公平鎖
Semaphore unfairLock = new Semaphore(5);
// 公平鎖
Semaphore fairLock = new Semaphore(5,true);

ReentrantLock

// 非公平鎖
ReentrantLock unfairLock = new ReentrantLock();
// 公平鎖
ReentrantLock fairLock = new ReentrantLock(true);

ReentrantReadWriteLock

// 非公平鎖
ReentrantReadWriteLock unfairLock = new ReentrantReadWriteLock();
// 公平可鎖
ReentrantReadWriteLock fairLock = new ReentrantReadWriteLock(true);

上面提到的 3 個(gè)鎖的實(shí)現(xiàn)類能配置公平鎖或者非公平鎖,真正實(shí)現(xiàn)鎖的公平與否是由AbstractQueuedSynchronizer抽象類的子類定義的贤笆。

3蝇棉、優(yōu)劣對(duì)比
獲取鎖事件 鎖的效率 備注
公平鎖 可以樂(lè)觀估計(jì) 相對(duì)較低
非公平鎖 饑餓狀態(tài) 相對(duì)較高 如果對(duì)鎖沒(méi)有特別的要求,優(yōu)先選用非公平鎖

公平鎖的效率比非公平鎖低的原因如下:

  • 所有想獲取鎖的線程必須先到先進(jìn)先出隊(duì)列注冊(cè)芥永,排隊(duì)才能獲取鎖篡殷,從獲取鎖的流程上增加額外的操作;
  • 有隊(duì)列必然涉及線程的阻塞與喚醒操作埋涧,增加了操作系統(tǒng)層次上下文切換調(diào)度開(kāi)銷贴唇。

(二)可重入鎖/非可重入鎖

可重入鎖是指某個(gè)線程獲得特定鎖后,同一個(gè)線程內(nèi)可以多次獲得該鎖飞袋。synchronized關(guān)鍵字、ReentrantLockReentrantReadWriteLock屬于可重入鎖链患,Jdk 內(nèi)置除此之外其它的鎖都是不可重入鎖巧鸭。

可重入鎖有兩個(gè)重要的特性:同一個(gè)線程、重復(fù)獲取鎖麻捻。

1纲仍、可重入鎖必要性分析

可重入鎖能夠避免同一線程多次獲取鎖時(shí)的死鎖現(xiàn)象呀袱。

/**
 * 競(jìng)爭(zhēng)線程調(diào)用入口方法
 */
public synchronized void facadeMethod(){
    // 處理業(yè)務(wù)
    innerMethod();
}
public void innerMethod(){
    // 處理業(yè)務(wù)
}

當(dāng)只在調(diào)用入口方法上添加 synchronized 鎖,內(nèi)部調(diào)用鏈所涉及的方法都不添加鎖郑叠,在線程競(jìng)爭(zhēng)條件下也是線程安全的夜赵。這種條件下即使 synchronized 不是可重入鎖,也不會(huì)發(fā)生死鎖乡革。原因如下:方法調(diào)用是以方法棧的形式調(diào)用的寇僧,在入口方法加鎖相當(dāng)于內(nèi)部調(diào)用鏈的方法都鎖的約束之下,因此是線程安全的沸版。

2嘁傀、非可重入鎖危害程度分析

假如 synchronized 不是可重入鎖,業(yè)務(wù)層有死鎖發(fā)生時(shí)视粮,應(yīng)用在測(cè)試環(huán)境壓測(cè)必然能夠發(fā)現(xiàn)细办,未進(jìn)入生產(chǎn)環(huán)境便可提前處理。因?yàn)檫@種死鎖是一種必然發(fā)生事件蕾殴,排查起來(lái)較為容易笑撞。

當(dāng)死鎖發(fā)生時(shí),第一步排查當(dāng)前鎖是否是可重入的钓觉,其次再考慮是否是業(yè)務(wù)層代碼邏輯本身存在缺陷茴肥。

/**
 * 競(jìng)爭(zhēng)線程調(diào)用入口方法
 */
public synchronized void facadeMethod(){
    // 處理業(yè)務(wù)
    innerMethod();
}
public synchronized void innerMethod(){
    // 處理業(yè)務(wù)
}

可重入鎖是對(duì)鎖的一次改良,提高了開(kāi)發(fā)效率是顯而易見(jiàn)的议谷,與此同時(shí)也給使用鎖的用戶造成不必要的困擾:在使用鎖的過(guò)程中炉爆,是否可重入并不是避免死鎖的充分條件。

(三)獨(dú)享鎖/共享鎖

獨(dú)享鎖是指該鎖一次只能被一個(gè)線程所持有卧晓;共享鎖是指該鎖可被多個(gè)線程所持有芬首。實(shí)現(xiàn)ReadWriteLock接口的鎖,其中讀鎖是共享鎖逼裆、寫(xiě)鎖是獨(dú)享鎖郁稍。

在內(nèi)置的鎖中,除了讀寫(xiě)鎖中的讀鎖是共享鎖胜宇,其余皆是獨(dú)享鎖耀怜。

1、降低鎖的顆粒度

競(jìng)爭(zhēng)線程在處理競(jìng)爭(zhēng)資源時(shí)有如下四種情形:讀讀桐愉、讀寫(xiě)财破、寫(xiě)讀、寫(xiě)寫(xiě)从诲,對(duì)于大部分應(yīng)用來(lái)說(shuō)左痢,讀操作的比寫(xiě)操作的頻度要高,更清楚的表述是在大部分時(shí)間里讀讀是線程間處理競(jìng)爭(zhēng)資源形態(tài),因此降低鎖的顆粒度現(xiàn)實(shí)意義比較明顯俊性。

2略步、共享讀鎖與樂(lè)觀讀鎖

共享讀鎖是為了解決獨(dú)占鎖只能被一個(gè)線程占有的問(wèn)題,它支持多個(gè)線程同時(shí)持有鎖定页,本質(zhì)上屬于悲觀鎖的范疇趟薄。

樂(lè)觀讀鎖更為徹底,將加鎖的環(huán)節(jié)取消典徊,但通過(guò)特殊機(jī)制仍能夠保證線程安全杭煎。

加鎖和釋放鎖是一個(gè)重操作,因此樂(lè)觀讀鎖比共享讀鎖效率更高宫峦。

鎖的匯總

// 非公平可重入讀寫(xiě)鎖
ReentrantReadWriteLock unfairLock = new ReentrantReadWriteLock();
// 公平可重入讀寫(xiě)鎖
ReentrantReadWriteLock fairLock = new ReentrantReadWriteLock(true);

(四)樂(lè)觀鎖/悲觀鎖

樂(lè)觀鎖與悲觀鎖的內(nèi)涵是當(dāng)并發(fā)發(fā)生時(shí)處理并發(fā)同步的態(tài)度岔帽。悲觀鎖認(rèn)為當(dāng)并發(fā)發(fā)生時(shí),被鎖的對(duì)象一定會(huì)發(fā)生修改导绷,如果放任不管犀勒,并發(fā)操作一定會(huì)給業(yè)務(wù)帶來(lái)副作用。

悲觀鎖需要加鎖妥曲,樂(lè)觀鎖不加鎖但仍能通過(guò)一定機(jī)制保證線程安全贾费。

互斥鎖、自旋鎖檐盟、讀寫(xiě)鎖都屬于悲觀鎖褂萧。

1、典型樂(lè)觀鎖

嚴(yán)格意義來(lái)講葵萎,只有悲觀鎖才能稱之為鎖闻鉴,樂(lè)觀鎖本身不通過(guò)加鎖來(lái)解決并發(fā)問(wèn)題魁衙,因此稱之為樂(lè)觀“鎖”更合適喜庞。

樂(lè)觀“鎖”處理并發(fā)問(wèn)題有兩種常見(jiàn)方式:一是以AtomicInteger為代表的原子操作類案狠,這種處理方式本身不加鎖,但仍能解決并發(fā)產(chǎn)生的問(wèn)題卷雕;二是樂(lè)觀鎖StampedLock類中的樂(lè)觀讀节猿。

(五)自旋鎖

自旋鎖是相對(duì)于互斥鎖而言的,本質(zhì)上屬于悲觀鎖的一種(仍然需要加鎖)漫雕。

1滨嘱、自旋鎖的原理

當(dāng)線程申請(qǐng)獲取鎖時(shí),發(fā)現(xiàn)已經(jīng)被其它線程占有浸间,此時(shí)不斷的循環(huán)嘗試獲取鎖太雨,直到獲取鎖成功。線程自旋獲取鎖需要消耗 CPU魁蒜,如果一直獲取不到鎖囊扳,線程會(huì)一直自旋煤墙,持續(xù)消耗 CPU。

自旋鎖是對(duì)線程申請(qǐng)獲取鎖時(shí)出現(xiàn)的阻塞與喚醒上下文切換的一種優(yōu)化宪拥,即用 CPU 資源換取線程狀態(tài)切換時(shí)間,當(dāng)線程通過(guò)自旋獲取鎖的時(shí)間超過(guò)普通的阻塞-喚醒調(diào)度時(shí)間铣减,那么就不適合選用自旋鎖她君。

2、自旋鎖使用場(chǎng)景及優(yōu)缺點(diǎn)

(1)使用場(chǎng)景

如果持有鎖的線程能在很短時(shí)間內(nèi)釋放鎖資源葫哗,選用自旋鎖非常合適缔刹。線程平均占有鎖的時(shí)間很短,其它線程稍微等待(自旋)便能立刻獲取鎖劣针,效率比阻塞-喚醒線程狀態(tài)切換高得多校镐。

一般而言,競(jìng)爭(zhēng)資源涉及內(nèi)存計(jì)算時(shí)捺典,占有鎖的時(shí)間平均都比較短鸟廓,適合自旋鎖;對(duì)于磁盤(pán)讀寫(xiě) IO 操作襟己、網(wǎng)絡(luò)操作等引谜,線程占有鎖的時(shí)間平均較長(zhǎng),不適合使用自旋鎖擎浴。

代碼塊或者輕量級(jí)方法员咽,線程競(jìng)爭(zhēng)不激烈的場(chǎng)景下,適合自旋鎖贮预。

(2)優(yōu)缺點(diǎn)

自旋鎖盡可能的減少線程的阻塞贝室,對(duì)于鎖的競(jìng)爭(zhēng)不激烈且占用鎖時(shí)間非常短的代碼塊來(lái)說(shuō)性能提升明顯。自旋的時(shí)間消耗會(huì)小于線程阻塞掛起再喚醒的操作的消耗仿吞,回避了線程兩次上下文切換滑频。

3、自旋鎖與樂(lè)觀鎖

自旋鎖與樂(lè)觀鎖的區(qū)別是很明顯的茫藏,很多地方常用 CAS 技術(shù)對(duì)兩者舉例误趴,以致于讓它們的邊界比較模糊。

樂(lè)觀(悲觀)鎖 獨(dú)占(共享)鎖 消耗 CPU 資源的目的 提升效率優(yōu)化核心點(diǎn)
自旋鎖 悲觀鎖 獨(dú)占鎖 申請(qǐng)獲取鎖 用 CPU 資源置換線程阻塞-喚醒調(diào)度時(shí)間
樂(lè)觀鎖 樂(lè)觀鎖 共享鎖 比較與交換 不加鎖务傲,如果需要處理線程問(wèn)題凉当,則采取相應(yīng)的措施

除了原子操作類中用樂(lè)觀鎖處理讀寫(xiě)外,StampedLock類主要用到樂(lè)觀讀鎖售葡。

三看杭、關(guān)鍵字鎖

synchronized關(guān)鍵字屬于內(nèi)置鎖,可作用于對(duì)象方法挟伙。添加到方法上的鎖楼雹,鎖到在哪里?

對(duì)于實(shí)例方法,鎖添加到持有方法的實(shí)例上贮缅;對(duì)于類方法榨咐,鎖添加到類對(duì)象(Class 對(duì)象)上。

(一)感性認(rèn)識(shí)

關(guān)鍵字synchronized創(chuàng)建的是一把可重入的鎖谴供,不是簡(jiǎn)單的輕量級(jí)或者重量級(jí)的鎖块茁,也不是簡(jiǎn)單的公平與非公平鎖。

Java8 內(nèi)置的synchronized是經(jīng)過(guò)優(yōu)化的鎖桂肌,有偏向鎖数焊、輕量級(jí)鎖、重量級(jí)鎖等狀態(tài)崎场。

重量級(jí)鎖影響性能的根本原因是伴隨著加鎖與釋放鎖佩耳,競(jìng)爭(zhēng)鎖的工作線程發(fā)生上下文切換。

1谭跨、公平性分析

鎖處于輕量級(jí)時(shí)干厚,因?yàn)椴淮嬖诰€程間獲取鎖的實(shí)質(zhì)性碰撞行為,理論情況下“競(jìng)爭(zhēng)”線程不存在饑餓狀態(tài)的發(fā)生饺蚊,因此屬于公平鎖萍诱。

鎖處于重量級(jí)時(shí),無(wú)法保證競(jìng)爭(zhēng)線程一定不存在饑餓狀態(tài)發(fā)生污呼,因此屬于非公平鎖裕坊。

2、非公平如何理解

使用 synchronized 加鎖的線程燕酷,沒(méi)有先進(jìn)先出的隊(duì)列機(jī)制保證有序獲取鎖籍凝,因此它是非公平鎖。

(1)競(jìng)爭(zhēng)線程隨機(jī)獲取鎖苗缩?

隨機(jī)必然伴隨著概率事件饵蒂,獲取鎖既有成功的概率也有失敗的概率,如果是嚴(yán)格隨機(jī)酱讶,理論情況下是不存在饑餓狀態(tài)發(fā)生的退盯,這種情況下也就不屬于非公平鎖之說(shuō)。

競(jìng)爭(zhēng)線程不是隨機(jī)獲取鎖泻肯,盡管從線程的角度看像是一種“隨機(jī)”行為渊迁,因此它是一把非公平鎖。

(2)競(jìng)爭(zhēng)線程可預(yù)測(cè)獲取鎖灶挟?

(重量級(jí)鎖)在競(jìng)爭(zhēng)鎖條件下必然存在操作系統(tǒng)級(jí)別的(線程阻塞與喚醒)系統(tǒng)調(diào)度行為琉朽。操作系統(tǒng)的調(diào)度是按照既定的規(guī)則進(jìn)行線程調(diào)度的,線程被操作系統(tǒng)喚醒稚铣,才有機(jī)會(huì)獲取鎖箱叁,因此可以粗略的理解獲取鎖的行為也是可以預(yù)測(cè)的墅垮。

(3)可預(yù)測(cè)獲取鎖是公平鎖?

假如操作系統(tǒng)是按照優(yōu)先級(jí)高低完成線程調(diào)度的耕漱,極端情況下算色,新申請(qǐng)獲取鎖的線程優(yōu)先級(jí)永遠(yuǎn)比等待隊(duì)列中線程優(yōu)先級(jí)要高,那么等待隊(duì)列必然會(huì)發(fā)生饑餓狀態(tài)螟够,因此盡管獲取鎖的行為是有規(guī)律的剃允、能夠預(yù)測(cè)的,它依然是非公平鎖齐鲤。

3、互斥鎖

互斥鎖即重量級(jí)鎖椒楣,互斥依靠通過(guò)操作系統(tǒng)來(lái)實(shí)現(xiàn)给郊。

互斥的表現(xiàn)形式如下:當(dāng)多線程競(jìng)爭(zhēng)資源條件下,未獲得鎖的其它線程均處于阻塞狀態(tài)捧灰,當(dāng)持有鎖的線程釋放鎖后淆九,阻塞狀態(tài)的線程被喚醒競(jìng)爭(zhēng)獲取鎖,未獲取成功的鎖繼續(xù)阻塞毛俏,如此循環(huán)炭庙。線程調(diào)度需要操作系統(tǒng)切換上下文,占用 CPU 時(shí)間煌寇,影響性能焕蹄。

操作系統(tǒng) CPU 時(shí)間片大致可分為兩類,一是工作時(shí)間阀溶;二是調(diào)度時(shí)間腻脏,調(diào)度時(shí)間越長(zhǎng)相應(yīng)的便會(huì)縮短工作時(shí)長(zhǎng)。

(二)鎖的膨脹

這里不講鎖的膨脹過(guò)程银锻,只講鎖在膨脹過(guò)程中涉及的中間狀態(tài)永品,以及如何理解。鎖的膨脹是單向的击纬,只能升級(jí)不能降級(jí)鼎姐。

1、偏向鎖

線程間不存在鎖的競(jìng)爭(zhēng)行為更振,至多只有一個(gè)線程有獲取鎖的需求炕桨,常見(jiàn)場(chǎng)景為單線程程序

2殃饿、輕量級(jí)鎖

線程間存在鎖的偽競(jìng)爭(zhēng)行為谋作,即同一時(shí)刻絕對(duì)不會(huì)存在兩個(gè)線程申請(qǐng)獲取鎖,各線程盡管都有使用鎖的需求乎芳,但是是交替使用鎖遵蚜。

3帖池、重量級(jí)鎖

線程間存在鎖的實(shí)質(zhì)性競(jìng)爭(zhēng)行為,線程間都有獲取鎖的需求吭净,但是時(shí)間不可交錯(cuò)睡汹,互斥鎖的阻塞等待。

四寂殉、接口鎖

(一)Lock

Lock是所有接口實(shí)現(xiàn)類的父類接口囚巴,定義了鎖操作的基本規(guī)范。

public interface Lock {
    // 阻塞等待獲取鎖
    void lock();
    // 阻塞等待獲取鎖(可相應(yīng)中斷)
    void lockInterruptibly() throws InterruptedException;
    // 非阻塞獲取鎖
    boolean tryLock();
    // 等待指定時(shí)間非阻塞獲取鎖
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    // 釋放鎖
    void unlock();
}

(二)StampedLock

1友扰、StampedLock優(yōu)勢(shì)
高性能

StampedLock在讀線程非常多而寫(xiě)線程較少的場(chǎng)景下性能非常高彤叉,樂(lè)觀讀鎖屬于無(wú)鎖編程,可以簡(jiǎn)單理解為沒(méi)有加鎖村怪。

回避寫(xiě)鎖饑餓

非公平讀寫(xiě)鎖在讀多寫(xiě)少的場(chǎng)景下可能發(fā)生寫(xiě)鎖饑餓秽浇,而在高并發(fā)的場(chǎng)景下,都會(huì)優(yōu)先使用非公平鎖甚负。StampedLock能解決這個(gè)矛盾問(wèn)題:既能使用非公平讀寫(xiě)鎖柬焕,又能回避寫(xiě)鎖饑餓。

回避寫(xiě)鎖饑餓的機(jī)制是能將任一讀鎖轉(zhuǎn)化為寫(xiě)鎖梭域。

2斑举、典型應(yīng)用
排它寫(xiě)鎖
/**
 * 排它寫(xiě)鎖(an exclusively locked method)
 */
void move(double deltaX, double deltaY) {
    long stamp = stampedLock.writeLock();
    try {
        x += deltaX;
        y += deltaY;
    } finally {
        stampedLock.unlockWrite(stamp);
    }
}

排它寫(xiě)鎖能安全的修改數(shù)據(jù),在為釋放鎖之前病涨,其它線程無(wú)法獲取鎖富玷。

此種方式可能會(huì)發(fā)生寫(xiě)鎖饑餓的情況。

排它寫(xiě)鎖(改進(jìn))

普通寫(xiě)鎖可能會(huì)發(fā)生寫(xiě)鎖饑餓既穆,下面方式能夠避免寫(xiě)鎖饑餓——讀鎖轉(zhuǎn)寫(xiě)鎖凌彬。

/**
 * 無(wú)饑餓寫(xiě)鎖
 */
void moveNoHunger(double deltaX, double deltaY) {
    long stamp = stampedLock.readLock();
    try {
        while (x == 0.0 && y == 0.0) {
            long ws = stampedLock.tryConvertToWriteLock(stamp);
            if (ws != 0L) {
                stamp = ws;
                x += deltaX;
                y += deltaY;
                break;
            } else {
                stampedLock.unlockRead(stamp);
                stamp = stampedLock.writeLock();
            }
        }
    } finally {
        stampedLock.unlock(stamp);
    }
}
樂(lè)觀鎖
/**
 * 樂(lè)觀讀鎖
 */
double distanceFromOrigin() { // A read-only method
    long stamp = stampedLock.tryOptimisticRead();
    double currentX = x, currentY = y;
    if (!stampedLock.validate(stamp)) {
        stamp = stampedLock.readLock();
        try {
            currentX = x;
            currentY = y;
        } finally {
            stampedLock.unlockRead(stamp);
        }
    }
    return Math.sqrt(currentX * currentX + currentY * currentY);
}

喜歡本文點(diǎn)個(gè)??贊??支持一下,如有需要循衰,可通過(guò)微信dream4s與我聯(lián)系铲敛。相關(guān)源碼在GitHub,視頻講解在B站会钝,本文收藏在博客天地伐蒋。


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市迁酸,隨后出現(xiàn)的幾起案子先鱼,更是在濱河造成了極大的恐慌,老刑警劉巖奸鬓,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焙畔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡串远,警方通過(guò)查閱死者的電腦和手機(jī)宏多,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)儿惫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人伸但,你說(shuō)我怎么就攤上這事肾请。” “怎么了更胖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵铛铁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我却妨,道長(zhǎng)饵逐,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任彪标,我火速辦了婚禮梳毙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捐下。我一直安慰自己,他們只是感情好萌业,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布坷襟。 她就那樣靜靜地躺著,像睡著了一般生年。 火紅的嫁衣襯著肌膚如雪婴程。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天抱婉,我揣著相機(jī)與錄音档叔,去河邊找鬼。 笑死蒸绩,一個(gè)胖子當(dāng)著我的面吹牛衙四,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播患亿,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼传蹈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了步藕?” 一聲冷哼從身側(cè)響起惦界,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咙冗,沒(méi)想到半個(gè)月后沾歪,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡雾消,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年灾搏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了挫望。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡确镊,死狀恐怖士骤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蕾域,我是刑警寧澤拷肌,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站旨巷,受9級(jí)特大地震影響巨缘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜采呐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一若锁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧斧吐,春花似錦又固、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蝶糯,卻和暖如春洋只,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背昼捍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工识虚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人妒茬。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓担锤,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親乍钻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子妻献,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

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