java并發(fā)編程實(shí)戰(zhàn)

每一個(gè)想學(xué)習(xí)Java多線程的人拗馒,手里至少有這本書(shū)或者至少要看這本書(shū)谤狡。強(qiáng)烈建議大家多看幾遍。


代碼中比較容易出現(xiàn)bug的場(chǎng)景:

不一致的同步蝠猬,直接調(diào)用Thread.run,未被釋放的鎖统捶,空的同步塊榆芦,雙重檢查加鎖柄粹,在構(gòu)造函數(shù)中啟動(dòng)一個(gè)線程,notifynotifyAll通知錯(cuò)誤匆绣,Object.waitCondition.await未在同步方法或塊中調(diào)用驻右,把Lock當(dāng)鎖用,調(diào)用Condition.wait方法崎淳,在休眠或等待時(shí)持有鎖堪夭,自旋循環(huán)。


1.多線程可以提高資源的利用率拣凹,可以充分利用現(xiàn)代多核處理器的特性森爽,讓每個(gè)線程負(fù)責(zé)處理同類型的任務(wù),更加容易維護(hù)嚣镜,同時(shí)通過(guò)異步處理提高響應(yīng)性爬迟。


2.多線程之間為更方便的實(shí)現(xiàn)數(shù)據(jù)共享采用了共享相同內(nèi)存地址空間的形式,并且是并發(fā)運(yùn)行菊匿,導(dǎo)致多個(gè)線程可能會(huì)同時(shí)訪問(wèn)或修改其他線程正在使用的變量值雕旨,導(dǎo)致安全性,同時(shí)如果線程之間相互等待對(duì)方擁有的鎖捧请,會(huì)出現(xiàn)活躍性即死鎖問(wèn)題。如果線程計(jì)算部分不多棒搜,更多的線程只會(huì)導(dǎo)致頻繁的切換上下文疹蛉,讓CPU的時(shí)間更多的花在線程調(diào)度而不是任務(wù)執(zhí)行上。


3.java同步的幾種方式:synchronized,volatile,顯示鎖,原子變量,線程及對(duì)象的基礎(chǔ)同步方法力麸。


4.所謂線程安全就是當(dāng)多個(gè)線程訪問(wèn)某個(gè)類時(shí)可款,不管運(yùn)行時(shí)環(huán)境采用何種調(diào)度方式或者這些線程將如何交替執(zhí)行,并且在主調(diào)代碼中不需要任何額外的同步或協(xié)同克蚂,這個(gè)類都能表現(xiàn)出正確的行為闺鲸。


5.將復(fù)合操作放在一個(gè)原子操作中執(zhí)行,或用相同的鎖來(lái)保護(hù)每個(gè)共享的和可變的變量。


6.增加同步必然會(huì)導(dǎo)致代碼的復(fù)雜性埃叭,為性能犧牲代碼簡(jiǎn)單性時(shí)不要太盲目摸恍,因?yàn)樵綇?fù)雜的代碼,其不安全性越大赤屋。


7.當(dāng)執(zhí)行時(shí)間較長(zhǎng)的計(jì)算或可能無(wú)法快速完成的操作時(shí),如網(wǎng)絡(luò)I/O立镶,一定不要持有鎖。


8.線程之間變量的讀取类早,在沒(méi)有同步的情況下媚媒,編譯器,處理器涩僻,以及運(yùn)行時(shí)都有可能對(duì)部分指令進(jìn)行重排導(dǎo)致并發(fā)問(wèn)題缭召。日常開(kāi)發(fā)中常見(jiàn)的set/get,如果沒(méi)有都加上synchronized,在多線程環(huán)境下也存在同樣的問(wèn)題栈顷。


9.對(duì)于非volatile的64位long或double,由于JVM允許對(duì)他們的讀取分解為高低32位來(lái)讀取,多線程下會(huì)發(fā)生只讀取部分32位的問(wèn)題,因此對(duì)這些變量,要用volatile或鎖保護(hù)讀取嵌巷。


10.對(duì)volatile變量,編譯器和運(yùn)行時(shí)不會(huì)將該變量上的操作與其他內(nèi)存操作一起重排序,也不會(huì)被緩存在寄存器或者對(duì)其他處理器不可見(jiàn)的地方,而是直接同步到內(nèi)存,保證其他線程讀取的時(shí)候返回最新寫(xiě)入的值萄凤。確切的說(shuō),volatile變量只保證可見(jiàn)性,對(duì)于自增或自減的操作并不能保證其原子性晴竞,因此不是線程安全的蛙卤。因此不要過(guò)多的依賴此對(duì)象,最好在滿足以下全部三個(gè)條件的情況下才考慮使用:

?(a)對(duì)變量的寫(xiě)入不依賴當(dāng)前變量的值,即所謂的不是自增或自減情況,或者可以保證只有單個(gè)線程對(duì)其更新

?(b)該變量不參與到不變性條件的判斷

?(c)訪問(wèn)該變量時(shí)不用加鎖

另一方面:當(dāng)且僅當(dāng)一個(gè)變量參與到包含其他狀態(tài)變量的不變性條件時(shí)噩死,才可以聲明為volatile類型颤难。


11.發(fā)布對(duì)象的幾種方式:1.將對(duì)象的引用保存到其他代碼中或public域中。?2.非私有方法返回該對(duì)象引用或該對(duì)象引用作為參數(shù)傳遞已维。?3.發(fā)布Collections組合. 4.通過(guò)已發(fā)布對(duì)象的非私有變量引用或方法獲取到的對(duì)象?5.類的內(nèi)部類實(shí)例隱含的包含了對(duì)該類實(shí)例的引用行嗤。


12.正確的或安全的發(fā)布一個(gè)對(duì)象,即是保證對(duì)象的引用以及對(duì)象的狀態(tài)必須同時(shí)對(duì)其他線程可見(jiàn)垛耳。不正確的發(fā)布可變對(duì)象會(huì)導(dǎo)致線程安全問(wèn)題栅屏,以下是一些保證變量或?qū)ο缶€程安全的方法:

??1.不要在構(gòu)造過(guò)程中使this逸出,即不要在構(gòu)造函數(shù)中創(chuàng)建并啟動(dòng)線程,不要調(diào)用可改寫(xiě)的方法,或注冊(cè)事件監(jiān)聽(tīng)或?qū)?nèi)部類實(shí)例化堂鲜。?2.多使用線程封閉,即盡量把對(duì)象放在單線程中不參與共享.?3.使用棧封閉栈雳,即在方法內(nèi)部用局部變量訪問(wèn)對(duì)象。?4.用ThreadLocal封裝變量缔莲,為每個(gè)線程提供一個(gè)只屬于該線程的變量副本哥纫。可以視ThreadLocal<T>為Map<Thread,T>,另外還有一個(gè)好處就是當(dāng)線程終止后痴奏,該值也會(huì)被回收蛀骇。(缺點(diǎn):ThreadLocal變量類似全局變量,會(huì)降低代碼的可重用性读拆,并在類之間引入隱含的耦合性)擅憔。?5.多用不可變對(duì)象(對(duì)象正確創(chuàng)建未this逸出,且創(chuàng)建后其狀態(tài)不能修改,且所有域都是final)檐晕,其一定是線程安全的暑诸。?6.在靜態(tài)初始化函數(shù)中初始化一個(gè)對(duì)象引用(JVM內(nèi)部的同步機(jī)制保證了這種發(fā)布方式的安全性) 7.將對(duì)象的引用保存到volatile類型的域,AtomicReferance對(duì)象辟灰,某個(gè)正確構(gòu)造對(duì)象的final域屠列,或一個(gè)由鎖保護(hù)的域中。?8.將對(duì)象放入線程安全的容器中可以由容器內(nèi)部的同步機(jī)制保障對(duì)象安全發(fā)布伞矩。


13.如果需要對(duì)一組數(shù)據(jù)以原子方式執(zhí)行某個(gè)操作,為避免競(jìng)態(tài)條件,可以創(chuàng)建一個(gè)不可變類來(lái)包含這些數(shù)據(jù)(如果數(shù)據(jù)是數(shù)組或其他可變對(duì)象笛洛,該類對(duì)應(yīng)的變量為clone的副本以保證不可變性),通過(guò)把這些數(shù)據(jù)保存到該不可變類的實(shí)例上乃坤,并且用volatile來(lái)確保該實(shí)例的可見(jiàn)性苛让,這樣可以保證線程操作數(shù)據(jù)的安全沟蔑。如果該類對(duì)象是可變的,當(dāng)然可以加鎖來(lái)確保原子性。


14.使用同步和封轉(zhuǎn)來(lái)保護(hù)對(duì)象狀態(tài)即變量的不變性條件及后驗(yàn)條件狱杰,使得相關(guān)變量必須在單個(gè)原子操作中進(jìn)行讀取或更新瘦材。換句話說(shuō),借助原子性與封裝性仿畸,滿足狀態(tài)變量的有效值或狀態(tài)轉(zhuǎn)化上的各種約束條件食棕,使得狀態(tài)變量有效轉(zhuǎn)換,是確保線程安全的有效手段错沽。


15.實(shí)例封閉即是將一個(gè)對(duì)象的所有訪問(wèn)代碼路徑都封裝到另一個(gè)對(duì)象里簿晓,可以通過(guò)類私有變量,局部變量千埃,單個(gè)線程里等方式憔儿,保證被封閉的對(duì)象不會(huì)逸出,不會(huì)超出它們既定的作用域放可。常見(jiàn)的例子如同步包裝器工作如Collections.synchronizedList對(duì)容器對(duì)象的唯一引用以實(shí)現(xiàn)將底層容器對(duì)象封閉從而達(dá)到線程安全的目的谒臼。


16.委托現(xiàn)有的同步容器來(lái)保障線程安全一般對(duì)針對(duì)"面"上的存取,如果類身包含復(fù)合操作耀里,則該類必須自己提供加鎖機(jī)制來(lái)保證這些復(fù)合操作的原子性蜈缤。


17.當(dāng)為現(xiàn)有的類添加一個(gè)原子操作時(shí),利用組合并用同一個(gè)鎖來(lái)保護(hù)同步操作可以實(shí)現(xiàn)冯挎。


18.同步容器類Vector劫樟,Hashtable,以及由Collections.synchronizedXxx等工廠方法包裝的同步封裝器類织堂,它們實(shí)現(xiàn)線程安全的方式是:將它們的狀態(tài)封裝起來(lái),并對(duì)每個(gè)公有方法都進(jìn)行同步奶陈,使得每次只有一個(gè)線程能訪問(wèn)容器的狀態(tài)易阳。因此如果基于這些共有方法衍生出了一些新操作,必須注意這些操作有可能不是原子的從而引發(fā)同步問(wèn)題吃粒。

缺點(diǎn)同步容器將所有對(duì)容器狀態(tài)的訪問(wèn)都串行化以實(shí)現(xiàn)它們的線程安全目的潦俺,因此當(dāng)多個(gè)線程同時(shí)競(jìng)爭(zhēng)鎖時(shí),吞吐量將嚴(yán)重降低徐勃,并發(fā)性能嚴(yán)重受到影響事示。(正是由于上述原因,java5后開(kāi)始提供多種并發(fā)容器來(lái)代替同步容器,以極大地提高伸縮性并降低風(fēng)險(xiǎn))


19.并發(fā)容器類java5提供了多種并發(fā)容器以代替同步容器的低并發(fā)性僻肖,并增加了一些常見(jiàn)的復(fù)合操作肖爵,如if-not-add,替換,以及條件刪除臀脏,使得這些復(fù)合操作原子化劝堪。列舉及大致說(shuō)明如下:

?a:ConcurrentHashMap:也是基于散列的Map冀自,利用粒度更細(xì)的分段鎖機(jī)制使得任意數(shù)量的讀取線程可以并發(fā)訪問(wèn)Map,并使得一定數(shù)量的寫(xiě)入線程可以并發(fā)的修改Map秒啦,從而在并發(fā)訪問(wèn)下實(shí)現(xiàn)更高的吞吐量熬粗。

?b:CopyOnWriteArrayList(Set):保留一個(gè)指向底層基礎(chǔ)數(shù)組的引用,每次修改對(duì)象時(shí)余境,都會(huì)復(fù)制創(chuàng)建并重新發(fā)布一個(gè)新的容器副本驻呐,由于復(fù)制底層數(shù)組需要一定的開(kāi)銷,因此這些容器僅適用于迭代操作遠(yuǎn)遠(yuǎn)多于修改操作的場(chǎng)景芳来。

?c:Queue:用來(lái)保存一組等待處理的元素含末,如傳統(tǒng)FIFO的ConcurrentLinkedQueue,(非同步的)優(yōu)先隊(duì)列PriorityQueue,還有其他的,可參見(jiàn)API

?d:BlockingQueue:可阻塞的Queue绣张,如LinkedBlockingQueue,ArrayBlockingQueue,PriorityBlockingQueue,以及無(wú)存儲(chǔ)容量的SynchronousQueue(該容器的put和take方法會(huì)一致堵塞,直到有另一個(gè)線程已經(jīng)準(zhǔn)備好參與到交付過(guò)程答渔,因此僅當(dāng)有足夠多的消費(fèi)者,并且總是有一個(gè)消費(fèi)者準(zhǔn)備好獲取交付工作時(shí),才適合使用此同步隊(duì)列)侥涵。所有這些阻塞隊(duì)列適用于生產(chǎn)者-消費(fèi)者模式沼撕。

?e:Deque及BlockingDeque:java6新增的雙端及可阻塞雙端隊(duì)列,適用于工作密取模式芜飘。每個(gè)消費(fèi)者擁有各自的雙端隊(duì)列务豺,當(dāng)完成自己的隊(duì)列是可以從其他隊(duì)列的尾部開(kāi)始獲取工作,從而減少競(jìng)爭(zhēng)提供并發(fā)嗦明。


20.在同步容器顯式迭代(for-each,Iterator)或隱式迭代(toString,hashCode,equals,contailsAll,removeAll,retainAll)過(guò)程中笼沥,修改容器會(huì)出現(xiàn)ConcurrentModificationExcepiton異常。但是在并發(fā)容器中娶牌,它們提供的迭代器不會(huì)拋出ConcurrentModificationException異常奔浅,因此不需要在迭代過(guò)程中對(duì)容器加鎖。


21.同步工具類它們提供了一些特定的結(jié)構(gòu)化屬性,封裝了一些決定線程等待還是執(zhí)行的狀態(tài),并提供了操作這些狀態(tài)以及高效的等待同步工具類進(jìn)入預(yù)期狀態(tài)的方法诗良。主要包括:

?a:CountDownLatch閉鎖

?b:FutureTask

?c:Semaphore信號(hào)量

?d:CyclicBarrier柵欄


22.并發(fā)任務(wù)的抽象汹桦,首先是要找到單個(gè)任務(wù)的邊界,盡量使得各任務(wù)相互獨(dú)立鉴裹,任務(wù)之間不相互依賴舞骆,大多數(shù)服務(wù)器應(yīng)用都采用了自然的任務(wù)邊界,即以獨(dú)立的客戶請(qǐng)求為邊界径荔。一般來(lái)說(shuō)每項(xiàng)任務(wù)還應(yīng)該表示應(yīng)用程序的一小部分處理能力督禽,從而使整個(gè)應(yīng)用程序表現(xiàn)出更好的吞吐量和響應(yīng)性。


23.如果任務(wù)的執(zhí)行時(shí)間較長(zhǎng)总处,創(chuàng)建過(guò)多的線程不僅會(huì)耗費(fèi)JVM時(shí)間狈惫,消耗更多的內(nèi)存資源,而且大量的線程將競(jìng)爭(zhēng)CPU的有限資源鹦马,另外線程棧的地址空間也會(huì)限制創(chuàng)建過(guò)多的線程虱岂。


24.各種線程池的創(chuàng)建方式及各自的一些特點(diǎn):

?a)newFixedThreadPool:固定長(zhǎng)度的線程池玖院,如果某個(gè)線程發(fā)生了未預(yù)期的Exception而結(jié)束,線程池會(huì)補(bǔ)償一個(gè)新的線程第岖。

?b)newCachedThreadPool:動(dòng)態(tài)可緩存的線程池,如果當(dāng)前線程池規(guī)模超過(guò)處理需求难菌,則回收空閑線程,否則添加新線程蔑滓。

?c)newSingleThreadExecuto:創(chuàng)建單個(gè)工作者線程來(lái)執(zhí)行任務(wù)郊酒,如果這個(gè)線程異常結(jié)束,則會(huì)創(chuàng)建另一個(gè)線程來(lái)替代键袱×蔷剑可以確保依照任務(wù)在隊(duì)列中的順序串行執(zhí)行(FIFO,LIFO,優(yōu)先級(jí)等)

?d)newScheduledThreadPool:以延時(shí)或定時(shí)方式來(lái)執(zhí)行任務(wù)的固定長(zhǎng)度線程池。


25.ExecutorService擴(kuò)展了Executor接口以提供解決執(zhí)行服務(wù)生命周期的問(wèn)題蹄咖,ExecutorService生命周期有三種狀態(tài):運(yùn)行褐健,關(guān)閉和已終止。shutdown方法平緩關(guān)閉:不再接受新的任務(wù)澜汤,并等待已經(jīng)提交的以及尚未開(kāi)始執(zhí)行的任務(wù)執(zhí)行完成蚜迅。?shutdownNow方法粗暴關(guān)閉:嘗試取消所有運(yùn)行中的任務(wù),并且丟棄隊(duì)列中尚未開(kāi)始執(zhí)行的任務(wù)俊抵。


26.ExecutorService關(guān)閉后提交的任務(wù)交由Rejected Execution Handler來(lái)處理谁不,它會(huì)拋棄任務(wù)或使得execute方法拋出一個(gè)未檢查的RejectedExecutionException』栈澹可以調(diào)用awaitTermination(通常調(diào)用它后會(huì)立即調(diào)用shutdown)來(lái)等待ExecutorService到達(dá)終止?fàn)顟B(tài)刹帕,或者調(diào)用isTerminated來(lái)輪詢等待。


27.Timer類處理延遲任務(wù)與周期任務(wù)是有缺陷的谎替,一是它在執(zhí)行所有任務(wù)的時(shí)候只會(huì)創(chuàng)建一個(gè)線程偷溺,這樣一旦某個(gè)任務(wù)執(zhí)行時(shí)間超過(guò)間隔時(shí)間,后續(xù)任務(wù)將會(huì)連續(xù)執(zhí)行或被丟棄钱贯。另外更嚴(yán)重的是挫掏,如果某個(gè)TimeTask拋出了未檢查異常而終止了執(zhí)行線程,那么整個(gè)Timer將被取消喷舀。在Java5后,不要再使用Timer淋肾×蚵椋可以用DelayQueue與ScheduledThreadPoolExecutor組合構(gòu)建自己的調(diào)度服務(wù)。

擴(kuò)展:關(guān)于Timer與ScheduleExecutorService執(zhí)行Runnable任務(wù)是否拋出異常對(duì)程序的影響差異比較:


28.在Executor框架中樊卓,已提交但尚未開(kāi)始的任務(wù)可以取消拿愧,如果是已經(jīng)開(kāi)始執(zhí)行的任務(wù),只有當(dāng)它們能響應(yīng)中斷時(shí)才可以取消碌尔。Future.get如果拋出了異常浇辜,會(huì)封裝成ExecutionException券敌,可以通過(guò)getCause來(lái)獲取初始異常。


29.CompletionService將已經(jīng)完成的任務(wù)按照完成順序放置到其內(nèi)置的BlockingQueue隊(duì)列上柳洋,每次get取到的都是最新完成的任務(wù)結(jié)果待诅。可以用Callable<Void>來(lái)表示無(wú)返回的任務(wù)熊镣。


30.invokeAll按照任務(wù)集合中迭代器的順序?qū)⑺械腇uture添加到返回的集合中卑雁。invokeAll會(huì)等待所有任務(wù)完成或超時(shí)才返回結(jié)果,不像submit立即異步返回绪囱,因?yàn)閕nvokeAll內(nèi)部對(duì)FutureList做了循環(huán)get等待测蹲。


31.可以使用線程的中斷以及類庫(kù)中提供的中斷支持來(lái)實(shí)現(xiàn)任務(wù)的可取消特性。每個(gè)線程都有一個(gè)boolean類型的中斷狀態(tài)鬼吵,當(dāng)中斷線程時(shí)扣甲,此狀態(tài)將設(shè)置為true。關(guān)于線程中斷有三個(gè)方法:

?1)interrupt:線程實(shí)例方法,調(diào)用后中斷實(shí)例線程,設(shè)置該實(shí)例線程的中斷狀態(tài)為true

?2)isInterrupted:線程實(shí)例方法,返回實(shí)例的中斷狀態(tài)ture/false

?3)interrupted:靜態(tài)方法齿椅,將清除當(dāng)前線程的中斷狀態(tài)琉挖,并返回線程之前的狀態(tài)。注意:如果調(diào)用此方法清除當(dāng)前線程的中斷狀態(tài)并返回了true媒咳,說(shuō)明當(dāng)前線程在中斷之前就已經(jīng)是"已中斷"的狀態(tài)了粹排,如果你不做任何處理,那么之前的中斷就被屏蔽掉了涩澡,可以通過(guò)拋出InterruptedException來(lái)響應(yīng)中斷或再次調(diào)用interrupt來(lái)恢復(fù)中斷顽耳。

一旦一個(gè)線程被終止或正常結(jié)束,都不能再次調(diào)用start方法啟動(dòng)了妙同,否則會(huì)拋出InvalidThreadStateException.

當(dāng)一個(gè)方法由于等待某個(gè)條件變成真而阻塞時(shí)射富,需要提供一種取消機(jī)制。


32.常見(jiàn)的阻塞方法如Thread.sleep,Object.wait在阻塞時(shí)都會(huì)檢查線程是否已中斷粥帚,如果發(fā)現(xiàn)已中斷胰耗,則會(huì)先清除中斷狀態(tài),然后拋出InterruptException.(通常芒涡,可中斷的方法會(huì)在阻塞或進(jìn)行重要的工作前首先檢查中斷柴灯,以便能盡快的響應(yīng)中斷)因此,在線程里調(diào)用可拋出InterruptedException異常的阻塞方法時(shí)將使線程處于某種阻塞狀態(tài)费尽,如果這個(gè)方法被中斷赠群,那么它將努力提前結(jié)束阻塞狀態(tài)。當(dāng)我們調(diào)用這些阻塞方法時(shí)旱幼,最好遵循下面的兩種方法之一:1)拋出此InterruptedException異常查描。?2)如果無(wú)法拋出異常,比如在Runnable中運(yùn)行,那么也一定要捕獲此異常冬三,并調(diào)用當(dāng)前線程上的interrupt方法恢復(fù)中斷狀態(tài),使調(diào)用棧中的更高層代碼看到此線程引發(fā)了中斷匀油。?最好不要捕獲異常又不做任何響應(yīng),這樣調(diào)用棧上的高層代碼無(wú)法對(duì)中斷采取處理措施勾笆,因?yàn)榫€程被中斷的證據(jù)已經(jīng)丟失了敌蚜。


33.在非阻塞的狀態(tài)下,如果調(diào)用線程實(shí)例的interrupt方法匠襟,只是設(shè)置了該實(shí)例的中斷狀態(tài)钝侠,并不會(huì)拋出InterruptException,因此如果你的代碼沒(méi)有明確觸發(fā)InterruptException的地方酸舍,也就意味著該線程實(shí)例沒(méi)有很好的響應(yīng)中斷帅韧,只是此中斷狀態(tài)將一直保持,直到調(diào)用interrupted明確清除之啃勉。


34.對(duì)于一些不支持取消但仍會(huì)調(diào)用可阻塞的方法操作忽舟,必須在循環(huán)中調(diào)用這些阻塞方法,并在發(fā)現(xiàn)中斷后重新嘗試調(diào)用淮阐,當(dāng)然當(dāng)這些方法檢測(cè)到已中斷會(huì)拋出InterruptException叮阅,應(yīng)該記錄這個(gè)狀態(tài),并在返回前調(diào)用interrupt恢復(fù)中斷泣特。永遠(yuǎn)不要在方法中調(diào)用"調(diào)用線程(宿主線程)"的interrupt,因?yàn)槟銦o(wú)法知道當(dāng)前線程的中斷策略浩姥,最好的方式是在方法內(nèi)創(chuàng)建一個(gè)線程,并對(duì)它進(jìn)行中斷状您,因?yàn)槟憧梢钥刂扑闹袛嗖呗浴?/p>


35.當(dāng)Future.get拋出InterruptException或TimeoutException時(shí)勒叠,如果你知道不再需要結(jié)果了,就可以調(diào)用Future.cancel來(lái)取消任務(wù)膏孟。


36.對(duì)于不可取消中斷的阻塞眯分,如Socket IO, File IO,等待內(nèi)置鎖,可以通過(guò)封裝Thread或用newTaskFor柒桑,將阻塞方法的不可中斷性轉(zhuǎn)移到其能響應(yīng)的異常上弊决,如通過(guò)提供封裝后的cancel方法,將不可中斷的Socket IO讀寫(xiě)方法在cancel中變?yōu)殛P(guān)閉Socket,這樣read或write將拋出IOException魁淳,這樣可以將原本應(yīng)該拋出InterruptException轉(zhuǎn)變成了IOException.


37.對(duì)于非正常終止的線程飘诗,比如拋出了RuntimeException,如果想做一些清理工作界逛,可以有兩種方式昆稿,一是設(shè)置線程的setUncaughtExceptionHandler,通過(guò)一個(gè)實(shí)現(xiàn)Thread.UncaughtExceptionHandler接口的類做一些清理仇奶,另一個(gè)是在線程啟動(dòng)時(shí)注冊(cè)一個(gè)關(guān)閉鉤子Runtime.getRuntime().addShutdownHook貌嫡,這樣虛擬機(jī)在關(guān)閉的時(shí)候就會(huì)執(zhí)行這些鉤子方法。


38.Executor框架可以將任務(wù)的提交與任務(wù)的執(zhí)行策略解耦開(kāi)來(lái)该溯,但是以下情形卻需要明確指定執(zhí)行策略以保障安全性及避免活躍性問(wèn)題:

?a)依賴性任務(wù):提交給線程池的任務(wù)需要依賴其他任務(wù),則會(huì)隱含的約束執(zhí)行策略

?b)單線程環(huán)境任務(wù):?jiǎn)尉€程的Executor下執(zhí)行任務(wù)隱含的使用線程封閉機(jī)制保障了線程安全岛抄,如果切換到多線程環(huán)境下,會(huì)可能導(dǎo)致并發(fā)

?c)對(duì)時(shí)間響應(yīng)敏感的任務(wù):如果這些任務(wù)與其他時(shí)間較長(zhǎng)的任務(wù)同時(shí)提交給線程池狈茉,在單線程及包含少量線程的Executor下會(huì)影響敏感任務(wù)的執(zhí)行

?d)使用ThreadLocal的任務(wù):由于線程池會(huì)動(dòng)態(tài)的回收或增加線程夫椭,因此“只有當(dāng)線程本地址的生命周期受限于任務(wù)的生命周期時(shí),在線程池中的線程使用ThreadLocal才有意義”氯庆,而且不應(yīng)該使用ThreadLocal在任務(wù)之間傳遞值蹭秋。


39.只要線程池中的任務(wù)需要無(wú)限期的等待一些必須由池中其他任務(wù)才能提供的資源或條件,除非線程池足夠大堤撵,否則將發(fā)生饑餓死鎖.因此線程池中最好運(yùn)行那些同類型并且相互獨(dú)立的任務(wù)仁讨,以使線程池達(dá)到最大性能。如果確實(shí)需要執(zhí)行不同類型的任務(wù)实昨,應(yīng)該考慮使用多個(gè)線程池洞豁。


40.線程池設(shè)置大小公式:Nthread =?Ncpu?* Ucpu(cpu目標(biāo)利用率) * (1+W/C(等待時(shí)間與計(jì)算時(shí)間比))。對(duì)于計(jì)算密集型任務(wù)荒给,在擁有N個(gè)處理器的系統(tǒng)上丈挟,當(dāng)線程池的大小為N+1時(shí),通常能實(shí)現(xiàn)最優(yōu)利用率志电。如果是其他資源限制曙咽,那么用該資源的總量除以每個(gè)任務(wù)對(duì)該資源的需求量,所得結(jié)果就是線程池大小的上限挑辆。

Amdahl定律:

并發(fā)后的加速比?<=1/(F+(1-F)/N)

其中F為串行計(jì)算部分的百分比例朱,N為CPU數(shù)


41.線程池的基本大小即沒(méi)有任務(wù)執(zhí)行時(shí)線程池的大小,只有在工作隊(duì)列已滿才會(huì)創(chuàng)建超出這個(gè)數(shù)量的線程之拨。如果某個(gè)線程超過(guò)了存活時(shí)間茉继,該線程被標(biāo)記為可回收,如果同時(shí)當(dāng)前線程池大小超過(guò)基本大小蚀乔,該線程將被終止烁竭。


42.對(duì)于沒(méi)有使用SynchronousQueue作為工作隊(duì)列的線程池(newCacheThreadPool默認(rèn)使用該隊(duì)列),如果線程池中的線程數(shù)量等于基本大小吉挣,僅當(dāng)隊(duì)列已滿時(shí)才會(huì)創(chuàng)建新的線程派撕,因此如果設(shè)置基本大小為0且隊(duì)列未滿,任務(wù)達(dá)到后先進(jìn)入隊(duì)列睬魂,由于此時(shí)線程數(shù)為0因此不會(huì)執(zhí)行任務(wù)终吼,只有待隊(duì)列滿時(shí)才會(huì)真正執(zhí)行任務(wù)。


43.基本任務(wù)排隊(duì)方法及被何種線程池采用:

?a:無(wú)界隊(duì)列:如無(wú)界LinkedBlockingQueue(FIFO),newFixedThreadPool和newSingleThreadExecutor默認(rèn)使用此隊(duì)列,此隊(duì)列的好處是能讓所有線程池中的線程保持忙碌狀態(tài)氯哮,缺點(diǎn)是一旦生產(chǎn)大于消費(fèi),隊(duì)列無(wú)限制增大耗盡內(nèi)存际跪。

?b:有界隊(duì)列:如ArrayBlockingQueue(FIFO),有界的LinkedBlockingQueue(FIFO),PriorityBlockingQueue(任務(wù)按自然順序或?qū)崿F(xiàn)Comparable排序)。當(dāng)有界隊(duì)列滿后會(huì)根據(jù)飽和策略處理.

?c:同步移交(Synchronous Handoff):SynchronousQueue姆打,必須有空閑線程(或還能創(chuàng)建新線程)等待接受時(shí)才可以良姆,否則拒絕。一般只用在線程池?zé)o界或可以拒絕任務(wù)時(shí)幔戏,如在newCachedThreadPool中(由于運(yùn)用了此隊(duì)列玛追,因此它能比固定大小的線程池提供更好的排隊(duì)性能,特別是Java6優(yōu)化了非阻塞算法,因此只要不是受特殊資源限制,都建議用newCachedThreadPool作為默認(rèn)的選擇).


44.四種飽和策略(ThreadPoolExecutor可以調(diào)用setRejectedExecutionHandler來(lái)設(shè)置):

?a)中止策略:默認(rèn)策略,拋出未檢查的RejectedExecutionException.

?b)拋棄策略:放棄該新任務(wù)

?c)拋棄最舊的策略:根據(jù)FIFO,最舊的就是下一個(gè)將被執(zhí)行的任務(wù)被拋棄以嘗試提交新的任務(wù)闲延。因此一般該策略不和FIFO一起用痊剖。

?d)調(diào)用者運(yùn)行策略:即在調(diào)用了execute的主線程中執(zhí)行,這樣在一段時(shí)間內(nèi)無(wú)暇再接受新的任務(wù)垒玲,新到達(dá)的請(qǐng)求停留在TCP層陆馁,如果持續(xù)過(guò)載,TCP層也會(huì)拋棄請(qǐng)求的,從而實(shí)現(xiàn)一種平緩的性能降低。其過(guò)程為:線程池--->工作隊(duì)列---->應(yīng)用程序--->TCP層--->客戶端合愈。


45.可以通過(guò)實(shí)現(xiàn)ThreadFactory接口以及繼承Thread類來(lái)自己定義如何產(chǎn)生新線程.比如可以加入日志,調(diào)用setUncaughExceptionHandler來(lái)設(shè)置該線程由于未捕獲到異常而突然終止時(shí)調(diào)用的處理程序氮惯。


46.當(dāng)然也可以調(diào)用Executors中的unconfigurableExecutorService封裝一個(gè)具體的ExecutorService以隱藏對(duì)ThreadPoolExecutor的配置。另外如果繼承ThreadPoolExecutor想暗,可以更靈活的擴(kuò)張線程池妇汗,主要有以下幾個(gè)方法:

?a)beforeExecute:任務(wù)執(zhí)行前調(diào)用,如果拋出異常,則任務(wù)不被執(zhí)行,此任務(wù)結(jié)束

?b)afterExecute:只要任務(wù)在完成后不是帶有一個(gè)Error,不管是正常返回還是拋出一個(gè)異常都會(huì)被執(zhí)行。

?c)terminated:所有任務(wù)已經(jīng)完成且所有工作者線程關(guān)閉后調(diào)用,可以用來(lái)釋放期生命期分配的資源及發(fā)送通知说莫,記錄日志杨箭,收集統(tǒng)計(jì)信息等。


47.多線程很重要的一個(gè)應(yīng)用是將“多個(gè)迭代之間彼此獨(dú)立储狭,而且每個(gè)迭代操作執(zhí)行的工作量比管理一個(gè)新任務(wù)開(kāi)銷大的”串行計(jì)算轉(zhuǎn)換為并行互婿,多個(gè)線程同時(shí)獨(dú)立計(jì)算各部分結(jié)果,當(dāng)某一個(gè)線程得到結(jié)果時(shí)辽狈,通過(guò)設(shè)置一個(gè)公共同步變量或synchronized方法來(lái)通知不需再產(chǎn)生新的任務(wù)并可以通知其他任務(wù)線程適當(dāng)結(jié)束自己慈参。當(dāng)然,也要考慮實(shí)在沒(méi)有結(jié)果的情況刮萌,為避免永遠(yuǎn)等待結(jié)果驮配,可以設(shè)置一個(gè)同步計(jì)數(shù)器,每個(gè)處理任務(wù)結(jié)束時(shí)在finally塊先將計(jì)數(shù)器減一并查看當(dāng)前剩余工作線程是否為0着茸,為0則表示無(wú)結(jié)果壮锻,可以設(shè)置一個(gè)空結(jié)果。


并發(fā)的不良后果-活躍性故障

48.活躍性故障最常見(jiàn)的是鎖順序死鎖涮阔,即兩個(gè)線程試圖以不同的順序來(lái)獲得相同的鎖猜绣,或者多個(gè)線程相互持有彼此正在等待的鎖而又不釋放自己已持有的鎖時(shí)會(huì)發(fā)生死鎖情況。包括以下幾種情況敬特;1:不同方法中鎖的順序不一樣掰邢,A方法先鎖Key1牺陶,再鎖Key2,而B(niǎo)方法先鎖Key2辣之,再鎖Key1. 2:方法中對(duì)傳入的參數(shù)加鎖义图,如果兩個(gè)參數(shù)類型相同,當(dāng)不同地方的調(diào)用這個(gè)方法傳入的參數(shù)順序相反召烂,如fromAccount,toAccount 3:協(xié)作對(duì)象間加鎖方法相互調(diào)用出現(xiàn)隱秘的死鎖娃承。解決死鎖有以下幾種方法奏夫;1:通過(guò)對(duì)象的hash值判斷鎖順序從而保證鎖順序一致?2:增加加時(shí)鎖?3:減小同步加鎖的代碼塊,將方法級(jí)加鎖改為代碼塊級(jí)加鎖历筝,盡量通過(guò)良好的線程封裝開(kāi)放調(diào)用酗昼。4.使用支持定時(shí)的鎖∈嶂恚可通過(guò)死鎖時(shí)轉(zhuǎn)儲(chǔ)的信息來(lái)分析死鎖發(fā)生的原因麻削。


49.活躍性故障另外兩個(gè)不良后果,一個(gè)是線程饑餓春弥,即由于線程優(yōu)先級(jí)的調(diào)整呛哟,導(dǎo)致有的線程始終無(wú)法得到cpu執(zhí)行,另一個(gè)是活鎖匿沛,即多個(gè)相互協(xié)作的線程都對(duì)彼此進(jìn)行響應(yīng)從而修改各自的狀態(tài)扫责,并使得任何一方都無(wú)法繼續(xù)。比如過(guò)度的錯(cuò)誤恢復(fù)代碼逃呼。一般在并發(fā)應(yīng)用中鳖孤,通過(guò)隨機(jī)等待長(zhǎng)度的時(shí)間和回退可以避免之。


并發(fā)的不良后果-線程調(diào)度開(kāi)銷

50.線程引入主要有以下開(kāi)銷:

?a:線程之間的協(xié)調(diào)(加鎖,觸發(fā)信號(hào),內(nèi)存同步)

?b:上下文切換(JVM和操作系統(tǒng)間,新開(kāi)線程的數(shù)據(jù)結(jié)構(gòu)加入緩存,阻塞線程被調(diào)度)

?c:內(nèi)存同步(如synchronized和volatile提供的可見(jiàn)性保證中會(huì)使用內(nèi)存柵欄刷新緩存,使緩存無(wú)效姐帚,刷新硬件的寫(xiě)緩沖以及停止執(zhí)行管道)

?d:阻塞(競(jìng)爭(zhēng)失敗的鎖無(wú)論是自旋等待還是被操作系統(tǒng)掛起)


51.對(duì)并發(fā)程序的測(cè)試包含安全性(正確性,)及活躍性(吞吐量爬早,響應(yīng)性吆寨,可伸縮性)。測(cè)試中盡量讓每個(gè)計(jì)算結(jié)果都被使用到平匈,并確保其值不可預(yù)測(cè),哪怕是與nanoTime任意的比較藏古,因?yàn)橐粋€(gè)智能編譯器將用預(yù)先計(jì)算的結(jié)果來(lái)代替計(jì)算過(guò)程吐葱。


52.對(duì)阻塞性的測(cè)試類似異常測(cè)試,如果代碼執(zhí)行到了阻塞方法的下面校翔,說(shuō)明測(cè)試失敗弟跑。(當(dāng)然也要注意測(cè)試時(shí)有方法解除阻塞)。對(duì)并發(fā)的測(cè)試防症,線程數(shù)應(yīng)該大于CPU數(shù)孟辑,如果要測(cè)試并發(fā)時(shí)存入取出元素的順序哎甲,可以利用元素hash和nanoTime加一些"移位"的方式求和比較。利用CyclicBarrier和CountDownLatch控制線程同步執(zhí)行饲嗽。


53.垃圾回收炭玫,java動(dòng)態(tài)編譯被多次執(zhí)行的代碼為機(jī)器碼,對(duì)代碼路徑不真實(shí)采樣,不真實(shí)的競(jìng)爭(zhēng)程度(測(cè)試時(shí)計(jì)算過(guò)多或過(guò)少)都會(huì)影響到測(cè)試代碼的效率貌虾。


54.內(nèi)置鎖無(wú)法在等待時(shí)中斷吞加,且必須在獲取該鎖的代碼塊中釋放。Lock提供了一種無(wú)條件的尽狠,可輪詢的衔憨,定時(shí)的,可中斷的鎖獲取操作袄膏。必須記住在finally塊中調(diào)用釋放鎖的操作践图。lockInterruptibly方法能夠在獲得鎖的同時(shí)保持對(duì)中斷的響應(yīng)。java6開(kāi)始內(nèi)置鎖的性能已經(jīng)與ReentranLock相當(dāng)了沉馆,但是ReentrantLock是非塊結(jié)構(gòu)的码党,獲取鎖的操作不能與特定的棧幀關(guān)聯(lián)。


55.當(dāng)持有鎖的時(shí)間相對(duì)較長(zhǎng)斥黑,或者請(qǐng)求鎖的平均時(shí)間間隔較長(zhǎng)揖盘,可以使用公平鎖,即當(dāng)鎖不可用時(shí)锌奴,新請(qǐng)求的線程將被放在隊(duì)列里而不是一有可用鎖立馬插隊(duì)扣讼。


56.讀寫(xiě)鎖的分離可以提高并發(fā),有一些選項(xiàng)需要注意:釋放優(yōu)先缨叫,讀線程插隊(duì)椭符,重入性,寫(xiě)降讀耻姥,讀升寫(xiě)销钝。


57.可以用內(nèi)置的條件隊(duì)列,顯式的Condition對(duì)象琐簇,以及AbstractQueueSynchronizer框架來(lái)構(gòu)造自己的同步器蒸健。


58.Object.wait會(huì)自動(dòng)釋放鎖,并請(qǐng)求操作系統(tǒng)掛起當(dāng)前線程婉商。在喚醒線程后似忧,wait在返回前還要重新獲取鎖。由于多個(gè)線程可能基于不同的條件(非空,已滿)在同一條件對(duì)象上等待丈秩,因此如果調(diào)用notify可能出現(xiàn)通知失誤盯捌,如已經(jīng)非空,但是notify通知卻喚醒了正在等待已滿狀態(tài)的wait蘑秽。一般來(lái)說(shuō)我們應(yīng)該用notifyAll饺著,除非同時(shí)滿足以下兩個(gè)條件1.所有等待線程的等待類型相同箫攀,2.單進(jìn)單出(在條件變量上的每次通知,最多只能喚醒一個(gè)線程)


59.內(nèi)置的條件隊(duì)列相關(guān)者Object中的wait,notify,notifyAll對(duì)于synchronized內(nèi)置鎖的關(guān)系幼衰,正如Condition中的await,signal,signalAll對(duì)于顯式的Condition靴跛,它們是一一配對(duì)使用的,因?yàn)楣芾頎顟B(tài)依賴性的機(jī)制必須與確保狀態(tài)一致性的機(jī)制關(guān)聯(lián)渡嚣。只不過(guò)后者提供了更多的控制梢睛,包括每個(gè)鎖對(duì)應(yīng)于多個(gè)等待線程集,可中斷或不可中斷的條件等待识椰,公平或非公平的隊(duì)列操作以及基于時(shí)限的等待绝葡。所有的阻塞等待都是在獲取到鎖的前提下進(jìn)行的,調(diào)用wait或await時(shí)釋放已經(jīng)得到的鎖并開(kāi)始等待裤唠,當(dāng)notify(notifyAll)或signal(signalAll)后被選擇執(zhí)行的線程開(kāi)始重新獲取鎖,并從等待處往下執(zhí)行莹痢。


60.AQS是一個(gè)構(gòu)建鎖和同步器的框架种蘸,其最基本的操作包括各種形式的獲取操作和釋放操作,其內(nèi)用一個(gè)整數(shù)來(lái)表示狀態(tài)信息竞膳,不同的同步實(shí)現(xiàn)無(wú)非用此整數(shù)外加一些輔助額外狀態(tài)變量來(lái)表示航瞭,如ReentrantLock用它來(lái)表示所有者線程已經(jīng)重復(fù)獲取該鎖的次數(shù),Semaphore用它來(lái)表示剩余的許可數(shù)量坦辟,F(xiàn)utureTask用它來(lái)表示任務(wù)的狀態(tài)刊侯,所有的具體同步實(shí)現(xiàn)都沒(méi)有直接擴(kuò)展AQS,而是將它們相應(yīng)的功能委托給私有的AQS子類锉走。

AQS還在內(nèi)部維護(hù)一個(gè)等待線程隊(duì)列滨彻,記錄某個(gè)線程請(qǐng)求的是獨(dú)占訪問(wèn)還是共享訪問(wèn)。


61.ReentrantReadWriteLock使用一個(gè)16位的狀態(tài)來(lái)表示寫(xiě)入鎖計(jì)數(shù)挪蹭,并使用獨(dú)占的獲取與釋放方法亭饵,并使用另一個(gè)16位的狀態(tài)來(lái)表示讀取的計(jì)數(shù),并用共享的獲取與釋放方法梁厉。因此當(dāng)鎖可用時(shí)辜羊,如果位于隊(duì)列頭部的線程執(zhí)行寫(xiě)入操作,那么線程會(huì)獲得這個(gè)鎖词顾,如果位于隊(duì)列的頭部執(zhí)行讀取線程八秃,那么隊(duì)列中的第一個(gè)寫(xiě)入線程之前所有的讀線程都將獲得此讀取鎖。(讀取優(yōu)先策略)


62.CAS的典型使用模式:首先從V中讀取值A(chǔ),并根據(jù)A計(jì)算新值B肉盹,然后再通過(guò)CAS以原子方式將V中的值由A變成B昔驱,其主要缺點(diǎn)是使調(diào)用者處理競(jìng)爭(zhēng)問(wèn)題(通過(guò)重試,回退上忍,放棄)舍悯,而在鎖中能自動(dòng)處理競(jìng)爭(zhēng)問(wèn)題(線程在獲得鎖之前將一直阻塞)航棱。


63.java的原子變量類AtomicXXX使用底層的CAS平臺(tái)指令為數(shù)字類型及引用類型提供一種高效的操作,java.util.concurrent包中的大多數(shù)類在實(shí)現(xiàn)時(shí)則直接或間接地使用了這些原子類萌衬。


64.創(chuàng)建非阻塞的算法關(guān)鍵在于找出將原子修改的范圍縮小到單個(gè)變量上饮醇,同時(shí)還要維護(hù)數(shù)據(jù)的一致性。

來(lái)自cnblogs

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末秕豫,一起剝皮案震驚了整個(gè)濱河市朴艰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌混移,老刑警劉巖祠墅,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異歌径,居然都是意外死亡毁嗦,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)回铛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)狗准,“玉大人,你說(shuō)我怎么就攤上這事茵肃∏怀ぃ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵验残,是天一觀的道長(zhǎng)捞附。 經(jīng)常有香客問(wèn)我,道長(zhǎng)您没,這世上最難降的妖魔是什么鸟召? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮氨鹏,結(jié)果婚禮上药版,老公的妹妹穿的比我還像新娘。我一直安慰自己喻犁,他們只是感情好槽片,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著肢础,像睡著了一般还栓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上传轰,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天剩盒,我揣著相機(jī)與錄音,去河邊找鬼慨蛙。 笑死辽聊,一個(gè)胖子當(dāng)著我的面吹牛纪挎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播跟匆,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼异袄,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了玛臂?” 一聲冷哼從身側(cè)響起烤蜕,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎迹冤,沒(méi)想到半個(gè)月后讽营,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泡徙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年橱鹏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堪藐。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡莉兰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出庶橱,到底是詐尸還是另有隱情贮勃,我是刑警寧澤贪惹,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布苏章,位于F島的核電站,受9級(jí)特大地震影響奏瞬,放射性物質(zhì)發(fā)生泄漏枫绅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一硼端、第九天 我趴在偏房一處隱蔽的房頂上張望并淋。 院中可真熱鬧,春花似錦珍昨、人聲如沸县耽。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)兔毙。三九已至,卻和暖如春兄春,著一層夾襖步出監(jiān)牢的瞬間澎剥,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工赶舆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哑姚,地道東北人祭饭。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像叙量,于是被迫代替她去往敵國(guó)和親倡蝙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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