壹朝巫、B站狂神juc
1、什么是JUC
java.util.concurrent 包是在并發(fā)編程中使用的工具類(lèi)石景,有以下三個(gè)包:
2.進(jìn)程和線程回顧
進(jìn)程 / 線程是什么劈猿?
進(jìn)程:進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合的一次運(yùn)行活動(dòng)。它是操作系統(tǒng)動(dòng)態(tài)執(zhí)行的基本單元潮孽,在傳統(tǒng)的操作系統(tǒng)中揪荣,進(jìn)程既是基本的分配單元,也是基本的執(zhí)行單元恩商。
線程:通常在一個(gè)進(jìn)程中可以包含若干個(gè)線程变逃,當(dāng)然一個(gè)進(jìn)程中至少有一個(gè)線程,不然沒(méi)有存在的意義怠堪,線程可以利用進(jìn)程所有擁有的資源揽乱。在引入線程的操作系統(tǒng)中,通常都是把進(jìn)程作為分配資源的基本單位粟矿,而把線程作為獨(dú)立運(yùn)行和獨(dú)立調(diào)度的基本單位凰棉,由于線程比進(jìn)程小,基本上不擁有系統(tǒng)資源陌粹,故對(duì)它的調(diào)度所付出的開(kāi)銷(xiāo)就會(huì)小得多撒犀,能更高效的提高系統(tǒng)多個(gè)程序間并發(fā)執(zhí)行的程度。
白話:
進(jìn)程:就是操作系統(tǒng)中運(yùn)行的一個(gè)程序,QQ.exe, music.exe或舞, word.exe 荆姆,這就是多個(gè)進(jìn)程
線程:每個(gè)進(jìn)程中都存在一個(gè)或者多個(gè)線程,比如用word寫(xiě)文章時(shí)映凳,就會(huì)有一個(gè)線程默默幫你定時(shí)自動(dòng)保存胆筒。
并發(fā) / 并行是什么?
做并發(fā)編程之前诈豌,必須首先理解什么是并發(fā)仆救,什么是并行。
線程的狀態(tài):Java的線程有6種狀態(tài):可以分析源碼:
wait / sleep 的區(qū)別????
3矫渔、Lock鎖
傳統(tǒng)的 synchronized
使用 juc.locks 包下的類(lèi)操作 Lock 鎖 + Lambda 表達(dá)式
synchronized 和 lock 區(qū)別
4彤蔽、生產(chǎn)者和消費(fèi)者
線程間的通信 , 線程之間要協(xié)調(diào)和調(diào)度
生產(chǎn)者和消費(fèi)者 synchroinzed 版 :
問(wèn)題升級(jí):防止虛假喚醒,4個(gè)線程庙洼,兩個(gè)加顿痪,兩個(gè)減
新版生產(chǎn)者和消費(fèi)者寫(xiě)法
、
閑聊常見(jiàn)筆試題:手寫(xiě)單例模式油够、手寫(xiě)冒泡排序员魏、手寫(xiě)生產(chǎn)者消費(fèi)者
精確通知順序訪問(wèn)
5、8鎖的現(xiàn)象
1叠聋、標(biāo)準(zhǔn)訪問(wèn),請(qǐng)問(wèn)先打印郵件還是短信受裹?
2碌补、郵件方法暫停4秒鐘,請(qǐng)問(wèn)先打印郵件還是短信棉饶?
3厦章、新增一個(gè)普通方法hello()沒(méi)有同步,請(qǐng)問(wèn)先打印郵件還是hello?
4照藻、兩部手機(jī)袜啃、請(qǐng)問(wèn)先打印郵件還是短信?
5幸缕、兩個(gè)靜態(tài)同步方法群发,同一部手機(jī),請(qǐng)問(wèn)先打印郵件還是短信发乔?
6熟妓、兩個(gè)靜態(tài)同步方法,2部手機(jī)栏尚,請(qǐng)問(wèn)先打印郵件還是短信起愈?
7、一個(gè)普通同步方法,一個(gè)靜態(tài)同步方法抬虽,同一部手機(jī)官觅,請(qǐng)問(wèn)先打印郵件還是短信?
8阐污、一個(gè)普通同步方法休涤,一個(gè)靜態(tài)同步方法,2部手機(jī)疤剑,請(qǐng)問(wèn)先打印郵件還是短信滑绒?
小結(jié)
6、集合類(lèi)不安全
list 不安全
單線程
多線程
set 不安全
map 不安全
7隘膘、Callable
多線程中疑故,第3種獲得多線程的方式邪铲,Callable第练。它與Runnable有什么區(qū)別呢冰肴?
怎么調(diào)用callable
8胰蝠、常用輔助類(lèi)
8.1见咒、CountDownLatch
8.2誊薄、CyclicBarrier
作用:和上面的減法相反冠蒋,這里是加法殉疼,好比集齊7個(gè)龍珠召喚神龍才漆,或者人到齊了再開(kāi)會(huì)牛曹!
8.3、Semaphore
翻譯:Semaphore 信號(hào)量;信號(hào)燈;信號(hào)作用:搶車(chē)位
9醇滥、讀寫(xiě)鎖
獨(dú)占鎖(寫(xiě)鎖):指該鎖一次只能被一個(gè)線程鎖持有黎比。對(duì)于ReentranrLock和 Synchronized 而言都是獨(dú)占鎖。
共享鎖(讀鎖):該鎖可被多個(gè)線程所持有鸳玩。
對(duì)于ReentrantReadWriteLock其讀鎖時(shí)共享鎖阅虫,寫(xiě)鎖是獨(dú)占鎖,讀鎖的共享鎖可保證并發(fā)讀是非常高效的不跟。
10.阻塞隊(duì)列
阻塞隊(duì)列
接口結(jié)構(gòu)圖
SynchronousQueue 同步隊(duì)列
11颓帝、線程池
池化技術(shù)
為什么使用線程池
10 年前單核CPU電腦,假的多線程窝革,像馬戲團(tuán)小丑玩多個(gè)球 购城,CPU 需要來(lái)回切換。現(xiàn)在是多核電腦聊闯,多個(gè)線程各自跑在獨(dú)立的CPU上工猜,不用切換效率高
ThreadPoolExecutor 七大參數(shù)
線程池用哪個(gè)?生產(chǎn)中如何設(shè)置合理參數(shù)?????
12菱蔬、四大函數(shù)式接口
java.util.function , Java 內(nèi)置核心四大函數(shù)式接口篷帅,可以使用lambda表達(dá)式
13史侣、Stream流式計(jì)算
流(Stream)到底是什么呢?
14魏身、分支合并
什么是ForkJoin
核心類(lèi)
15惊橱、異步回調(diào)
16、JMM
17箭昵、volatile
volatile是不錯(cuò)的機(jī)制税朴,但是也不能保證原子性
18、深入單例模式
? ??1家制、餓漢式
? ? 2.懶漢式
????3.靜態(tài)內(nèi)部類(lèi)
????4正林、萬(wàn)惡的反射
5.枚舉
19、深入理解CAS
CAS : 比較并交換
CAS 底層原理颤殴?如果知道觅廓,談?wù)勀銓?duì)UnSafe的理解?
問(wèn)題:這個(gè)UnSafe類(lèi)到底是什么涵但? 可以看到AtomicInteger源碼中也是它杈绸!
20、原子引用
原子類(lèi) AtomicInteger 的ABA問(wèn)題談?wù)劙粒吭痈乱弥绬幔????
原子引用 AtomicReference
要解決ABA問(wèn)題瞳脓,我們就需要加一個(gè)版本號(hào)
版本號(hào)原子引用,類(lèi)似樂(lè)觀鎖
演示ABA問(wèn)題:
解決方案:
21澈侠、Java鎖
貳劫侧、拉鉤多線程
并發(fā)編程簡(jiǎn)介
????java是一個(gè)支持多線程的開(kāi)發(fā)語(yǔ)言。多線程可以在包含多個(gè)CPU核心的機(jī)器上同時(shí)處理多個(gè)不同的任務(wù)哨啃,優(yōu)化資源的使用率板辽,提升程序的效率。在一些對(duì)性能要求比較高場(chǎng)合棘催,多線程是java程序調(diào)優(yōu)的重要方面。比較重要的內(nèi)容除了算法耳标,就是并發(fā)編程醇坝。并發(fā)編程是最能體現(xiàn)一個(gè)程序員功底的方面之一。
????Java并發(fā)編程主要涉及以下幾個(gè)部分:
1. 并發(fā)編程三要素
原子性:即一個(gè)不可再被分割的顆粒次坡。在Java中原子性指的是一個(gè)或多個(gè)操作要么全部執(zhí)行成功要么全部執(zhí)行失敗呼猪。
有序性:程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。(處理器可能會(huì)對(duì)指令進(jìn)行重排序)
可見(jiàn)性:當(dāng)多個(gè)線程訪問(wèn)同一個(gè)變量時(shí)砸琅,如果其中一個(gè)線程對(duì)其作了修改宋距,其他線程能立即獲取到最新的值。
2. 線程的五大狀態(tài)
創(chuàng)建狀態(tài):當(dāng)用 new 操作符創(chuàng)建一個(gè)線程的時(shí)候
就緒狀態(tài):調(diào)用 start 方法症脂,處于就緒狀態(tài)的線程并不一定馬上就會(huì)執(zhí)行 run 方法谚赎,還需要等待CPU的調(diào)度
運(yùn)行狀態(tài):CPU 開(kāi)始調(diào)度線程淫僻,并開(kāi)始執(zhí)行 run 方法
阻塞狀態(tài):線程的執(zhí)行過(guò)程中由于一些原因進(jìn)入阻塞狀態(tài)比如:調(diào)用 sleep 方法、嘗試去得到一個(gè)鎖等等
死亡狀態(tài):run 方法執(zhí)行完 或者 執(zhí)行過(guò)程中遇到了一個(gè)異常
3. 悲觀鎖與樂(lè)觀鎖
悲觀鎖:每次操作都會(huì)加鎖壶唤,會(huì)造成線程阻塞雳灵。
樂(lè)觀鎖:每次操作不加鎖而是假設(shè)沒(méi)有沖突而去完成某項(xiàng)操作,如果因?yàn)闆_突失敗就重試闸盔,直到成功為止悯辙,不會(huì)造成線程阻塞。
4. 線程之間的協(xié)作
線程間的協(xié)作有:wait/notify/notifyAll等
5. synchronized 關(guān)鍵字
synchronized是Java中的關(guān)鍵字迎吵,是一種同步鎖躲撰。它修飾的對(duì)象有以下幾種:
①修飾一個(gè)代碼塊:被修飾的代碼塊稱為同步語(yǔ)句塊,其作用的范圍是大括號(hào){}括起來(lái)的代碼击费,作用的對(duì)象是調(diào)用這個(gè)代碼塊的對(duì)象
②修飾一個(gè)方法:被修飾的方法稱為同步方法拢蛋,其作用的范圍是整個(gè)方法,作用的對(duì)象是調(diào)用這個(gè)方法的對(duì)象
③修飾一個(gè)靜態(tài)的方法:其作用的范圍是整個(gè)靜態(tài)方法荡灾,作用的對(duì)象是這個(gè)類(lèi)的所有對(duì)象
④修飾一個(gè)類(lèi):其作用的范圍是synchronized后面括號(hào)括起來(lái)的部分瓤狐,作用主的對(duì)象是這個(gè)類(lèi)的所有對(duì)象。
6. CAS
CAS全稱是Compare And Swap批幌,即比較替換础锐,是實(shí)現(xiàn)并發(fā)應(yīng)用到的一種技術(shù)。操作包含三個(gè)操作數(shù)—內(nèi)存位置(V)荧缘、預(yù)期原值(A)和新值(B)皆警。 如果內(nèi)存位置的值與預(yù)期原值相匹配,那么處理器會(huì)自動(dòng)將該位置值更新為新值 截粗。否則信姓,處理器不做任何操作。CAS存在三大問(wèn)題:ABA問(wèn)題绸罗,循環(huán)時(shí)間長(zhǎng)開(kāi)銷(xiāo)大意推,以及只能保證一個(gè)共享變量的原子操作。
7. 線程池
如果我們使用線程的時(shí)候就去創(chuàng)建一個(gè)線程珊蟀,雖然簡(jiǎn)單菊值,但是存在很大的問(wèn)題。如果并發(fā)的線程數(shù)量很多育灸,并且每個(gè)線程都是執(zhí)行一個(gè)時(shí)間很短的任務(wù)就結(jié)束了腻窒,這樣頻繁創(chuàng)建線程就會(huì)大大降低系統(tǒng)的效率,因?yàn)轭l繁創(chuàng)建線程和銷(xiāo)毀線程需要時(shí)間磅崭。線程池通過(guò)復(fù)用可以大大減少線程頻繁創(chuàng)建與銷(xiāo)毀帶來(lái)的性能上的損耗儿子。
常見(jiàn)的多線程問(wèn)題有:
1. 重排序有哪些分類(lèi)?如何避免砸喻?
2. Java 中新的Lock接口相對(duì)于同步代碼塊(synchronized block)有什么優(yōu)勢(shì)柔逼?如果讓你實(shí)現(xiàn)一
個(gè)高性能緩存蒋譬,支持并發(fā)讀取和單一寫(xiě)入,你如何保證數(shù)據(jù)完整性卒落。
3. 如何在Java中實(shí)現(xiàn)一個(gè)阻塞隊(duì)列羡铲。
4. 寫(xiě)一段死鎖代碼。說(shuō)說(shuō)你在Java中如何解決死鎖儡毕。
5. volatile變量和atomic變量有什么不同也切?
6. 為什么要用線程池?
7. 實(shí)現(xiàn)Runnable接口和Callable接口的區(qū)別
8. 執(zhí)行execute()方法和submit()方法的區(qū)別是什么呢腰湾?
9. AQS的實(shí)現(xiàn)原理是什么雷恃?
10. java API中哪些類(lèi)中使用了AQS?
11. ...
并發(fā)編程課程很多內(nèi)容會(huì)從JDK源碼解析相關(guān)原理费坊。
主要內(nèi)容包括:
1. 多線程&并發(fā)設(shè)計(jì)原理
并發(fā)核心概念
并發(fā)的問(wèn)題
JMM內(nèi)存模型
2. JUC
并發(fā)容器
同步工具類(lèi)
Atomic類(lèi)
Lock與Condition
3. 線程池與Future
線程池的實(shí)現(xiàn)原理
線程池的類(lèi)繼承體系
ThreadPoolExecutor
Executors工具類(lèi)
ScheduledThreadPool Executor
CompletableFuture用法4. ForkJoinPool
ForkJoinPool用法
核心數(shù)據(jù)結(jié)構(gòu)
工作竊取隊(duì)列
ForkJoinPool狀態(tài)控制
Worker線程的阻塞-喚醒機(jī)制
任務(wù)的提交過(guò)程分析
工作竊取算法:任務(wù)的執(zhí)行過(guò)程分析
ForkJoinTask的fork/join
ForkJoinPool的優(yōu)雅關(guān)閉
5. 多線程設(shè)計(jì)模式
Single Threaded Execution模式
Immutable模式
Guarded Suspension模式
Balking模式
Producer-Consumer模式
Read-Write Lock模式
Thread-Per-Message模式
Worker Thread模式
Future模式
一倒槐、多線程&并發(fā)設(shè)計(jì)原理
1 多線程回顧
1.1 Thread和Runnable
1.1.1 Java中的線程
創(chuàng)建執(zhí)行線程有兩種方法:1.擴(kuò)展Thread 類(lèi)。2.實(shí)現(xiàn)Runnable 接口附井。
1.1.2 Java中的線程:特征和狀態(tài)
1. 所有的Java 程序讨越,不論并發(fā)與否,都有一個(gè)名為主線程的Thread 對(duì)象永毅。執(zhí)行該程序時(shí)把跨, Java虛擬機(jī)( JVM )將創(chuàng)建一個(gè)新Thread 并在該線程中執(zhí)行main()方法。這是非并發(fā)應(yīng)用程序中唯一的線程沼死,也是并發(fā)應(yīng)用程序中的第一個(gè)線程着逐。
2. Java中的線程共享應(yīng)用程序中的所有資源,包括內(nèi)存和打開(kāi)的文件意蛀,快速而簡(jiǎn)單地共享信息耸别。但是必須使用同步避免數(shù)據(jù)競(jìng)爭(zhēng)。
3. Java中的所有線程都有一個(gè)優(yōu)先級(jí)县钥,這個(gè)整數(shù)值介于Thread.MIN_PRIORITY(1)和Thread.MAX_PRIORITY(10)之間秀姐,默認(rèn)優(yōu)先級(jí)是Thread.NORM_PRIORITY(5)。線程的執(zhí)行順序并沒(méi)有保證若贮,通常囊扳,較高優(yōu)先級(jí)的線程將在較低優(yōu)先級(jí)的錢(qián)程之前執(zhí)行。
4. 在Java 中兜看,可以創(chuàng)建兩種線程:
守護(hù)線程。
非守護(hù)線程狭瞎。
區(qū)別在于它們?nèi)绾斡绊懗绦虻慕Y(jié)束细移。
Java程序結(jié)束執(zhí)行過(guò)程的情形:
程序執(zhí)行Runtime類(lèi)的exit()方法, 而且用戶有權(quán)執(zhí)行該方法熊锭。
應(yīng)用程序的所有非守護(hù)線程均已結(jié)束執(zhí)行弧轧,無(wú)論是否有正在運(yùn)行的守護(hù)線程雪侥。
守護(hù)線程通常用在作為垃圾收集器或緩存管理器的應(yīng)用程序中,執(zhí)行輔助任務(wù)精绎。在線程start之前調(diào)用isDaemon()方法檢查線程是否為守護(hù)線程速缨,也可以使用setDaemon()方法將某個(gè)線程確立為守護(hù)線程宋光。
5. Thread.States類(lèi)中定義線程的狀態(tài)如下:
NEW:Thread對(duì)象已經(jīng)創(chuàng)建荤傲,但是還沒(méi)有開(kāi)始執(zhí)行。
RUNNABLE:Thread對(duì)象正在Java虛擬機(jī)中運(yùn)行呢燥。
BLOCKED : Thread對(duì)象正在等待鎖定搁吓。
WAITING:Thread 對(duì)象正在等待另一個(gè)線程的動(dòng)作原茅。
TIME_WAITING:Thread對(duì)象正在等待另一個(gè)線程的操作,但是有時(shí)間限制堕仔。
TERMINATED:Thread對(duì)象已經(jīng)完成了執(zhí)行擂橘。
getState()方法獲取Thread對(duì)象的狀態(tài),可以直接更改線程的狀態(tài)摩骨。
在給定時(shí)間內(nèi)通贞, 線程只能處于一個(gè)狀態(tài)。這些狀態(tài)是JVM使用的狀態(tài)恼五,不能映射到操作系統(tǒng)的線程狀態(tài)昌罩。
線程狀態(tài)的源碼:
1.1.3 Thread類(lèi)和Runnable 接口
Runnable接口只定義了一種方法:run()方法。這是每個(gè)線程的主方法唤冈。當(dāng)執(zhí)行start()方法啟動(dòng)新線程時(shí)峡迷,它將調(diào)用run()方法。
Thread類(lèi)其他常用方法:
獲取和設(shè)置Thread對(duì)象信息的方法:
①getId():該方法返回Thread對(duì)象的標(biāo)識(shí)符你虹。該標(biāo)識(shí)符是在錢(qián)程創(chuàng)建時(shí)分配的一個(gè)正整數(shù)绘搞。在線程的整個(gè)生命周期中是唯一且無(wú)法改變的。
②getName()/setName():這兩種方法允許你獲取或設(shè)置Thread對(duì)象的名稱傅物。這個(gè)名稱是一個(gè)String對(duì)象夯辖,也可以在Thread類(lèi)的構(gòu)造函數(shù)中建立。
③getPriority()/setPriority():你可以使用這兩種方法來(lái)獲取或設(shè)置Thread對(duì)象的優(yōu)先級(jí)董饰。
④isDaemon()/setDaemon():這兩種方法允許你獲取或建立Thread對(duì)象的守護(hù)條件蒿褂。
⑤getState():該方法返回Thread對(duì)象的狀態(tài)。
interrupt():中斷目標(biāo)線程卒暂,給目標(biāo)線程發(fā)送一個(gè)中斷信號(hào)啄栓,線程被打上中斷標(biāo)記。
interrupted():判斷目標(biāo)線程是否被中斷也祠,但是將清除線程的中斷標(biāo)記昙楚。
isinterrupted():判斷目標(biāo)線程是否被中斷,不會(huì)清除中斷標(biāo)記诈嘿。
sleep(long ms):該方法將線程的執(zhí)行暫停ms時(shí)間堪旧。
join():暫停線程的執(zhí)行削葱,直到調(diào)用該方法的線程執(zhí)行結(jié)束為止〈久危可以使用該方法等待另一個(gè)Thread對(duì)象結(jié)束析砸。
setUncaughtExceptionHandler():當(dāng)線程執(zhí)行出現(xiàn)未校驗(yàn)異常時(shí),該方法用于建立未校驗(yàn)異常的控制器爆袍。
currentThread():Thread類(lèi)的靜態(tài)方法首繁,返回實(shí)際執(zhí)行該代碼的Thread對(duì)象。
join示例程序(a程序在執(zhí)行螃宙,突然調(diào)用b程序的join方法就行執(zhí)行完b程序再執(zhí)行a程序):
1.1.4 Callable
Callable 接口是一個(gè)與Runnable 接口非常相似的接口蛮瞄。Callable 接口的主要特征如下。
①接口谆扎。有簡(jiǎn)單類(lèi)型參數(shù)挂捅,與call()方法的返回類(lèi)型相對(duì)應(yīng)。
②聲明了call()方法堂湖。執(zhí)行器運(yùn)行任務(wù)時(shí)闲先,該方法會(huì)被執(zhí)行器執(zhí)行。它必須返回聲明中指定類(lèi)型的對(duì)象无蜂。
③call()方法可以拋出任何一種校驗(yàn)異常伺糠。可以實(shí)現(xiàn)自己的執(zhí)行器并重載afterExecute()方法來(lái)處理這些異常斥季。
可以有返回值
可以處理任何異常
1.2 synchronized關(guān)鍵字
1.2.1 鎖的對(duì)象
synchronized關(guān)鍵字“給某個(gè)對(duì)象加鎖”训桶,示例代碼:
等價(jià)于=
實(shí)例方法的鎖加在對(duì)象myClass上;靜態(tài)方法的鎖加在MyClass.class上酣倾。
1.2.2 鎖的本質(zhì)
如果一份資源需要多個(gè)線程同時(shí)訪問(wèn)舵揭,需要給該資源加鎖。加鎖之后躁锡,可以保證同一時(shí)間只能有一個(gè)線程訪問(wèn)該資源午绳。資源可以是一個(gè)變量、一個(gè)對(duì)象或一個(gè)文件等映之。
1.2.3 實(shí)現(xiàn)原理
鎖如何實(shí)現(xiàn)拦焚?
在對(duì)象頭里,有一塊數(shù)據(jù)叫Mark Word杠输。在64位機(jī)器上赎败,Mark Word是8字節(jié)(64位)的,這64位中有2個(gè)重要字段:鎖標(biāo)志位和占用該鎖的thread ID蠢甲。因?yàn)椴煌姹镜腏VM實(shí)現(xiàn)僵刮,對(duì)象頭的數(shù)據(jù)結(jié)構(gòu)會(huì)有各種差異。
1.3 wait與notify
1.3.1 生產(chǎn)者?消費(fèi)者模型
生產(chǎn)者-消費(fèi)者模型是一個(gè)常見(jiàn)的多線程編程模型,如下圖所示:
1.如何阻塞妓笙?
辦法1:線程自己阻塞自己,也就是生產(chǎn)者能岩、消費(fèi)者線程各自調(diào)用wait()和notify()寞宫。
辦法2:用一個(gè)阻塞隊(duì)列,當(dāng)取不到或者放不進(jìn)去數(shù)據(jù)的時(shí)候拉鹃,入隊(duì)/出隊(duì)函數(shù)本身就是阻塞的辈赋。
2.如何雙向通知?
辦法1:wait()與notify()機(jī)制膏燕。
辦法2:Condition機(jī)制钥屈。
單個(gè)生產(chǎn)者單個(gè)消費(fèi)者線程的情形:
多個(gè)生產(chǎn)者單個(gè)消費(fèi)者線程的情形:
1.3.2 為什么必須和synchronized一起使用
在Java里面,wait()和notify()是Object的成員函數(shù)坝辫,是基礎(chǔ)中的基礎(chǔ)篷就。為什么Java要把wait()和notify()放在如此基礎(chǔ)的類(lèi)里面,而不是作為像Thread一類(lèi)的成員函數(shù)近忙,或者其他類(lèi)的成員函數(shù)呢竭业?
先看為什么wait()和notify()必須和synchronized一起使用?請(qǐng)看下面的代碼:
或者下面代碼????
1.3.3 為什么wait()的時(shí)候必須釋放鎖
1.3.4 wait()與notify()的問(wèn)題
以上述的生產(chǎn)者-消費(fèi)者模型來(lái)看及舍,其偽代碼大致如下:
生產(chǎn)者在通知消費(fèi)者的同時(shí)未辆,也通知了其他的生產(chǎn)者;消費(fèi)者在通知生產(chǎn)者的同時(shí)锯玛,也通知了其他消費(fèi)者咐柜。原因在于wait()和notify()所作用的對(duì)象和synchronized所作用的對(duì)象是同一個(gè),只能有一個(gè)對(duì)象攘残,無(wú)法區(qū)分隊(duì)列空和列隊(duì)滿兩個(gè)條件拙友。這正是Condition要解決的問(wèn)題。
1.4 InterruptedException與interrupt()方法
1.4.1 Interrupted異常
什么情況下會(huì)拋出Interrupted異常
假設(shè)while循環(huán)中沒(méi)有調(diào)用任何的阻塞函數(shù)肯腕,就是通常的算術(shù)運(yùn)算献宫,或者打印一行日志,如下所示实撒。
這個(gè)時(shí)候姊途,在主線程中調(diào)用一句thread.interrupt(),請(qǐng)問(wèn)該線程是否會(huì)拋出異常知态?不會(huì)捷兰。
只有那些聲明了會(huì)拋出InterruptedException的函數(shù)才會(huì)拋出異常,也就是下面這些常用的函數(shù):
本來(lái)sleep1000毫秒负敏,現(xiàn)在改變狀態(tài)為100毫秒贡茅,就拋出異常,但是一拋出異常,中斷狀態(tài)就變成false了
1.4.2 輕量級(jí)阻塞與重量級(jí)阻塞
能夠被中斷的阻塞稱為輕量級(jí)阻塞顶考,對(duì)應(yīng)的線程狀態(tài)是WAITING或者TIMED_WAITING赁还;而像synchronized 這種不能被中斷的阻塞稱為重量級(jí)阻塞,對(duì)應(yīng)的狀態(tài)是 BLOCKED驹沿。如圖所示:調(diào)用不同的方法后艘策,一個(gè)線程的狀態(tài)遷移過(guò)程。
????????初始線程處于NEW狀態(tài)渊季,調(diào)用start()開(kāi)始執(zhí)行后朋蔫,進(jìn)入RUNNING或者READY狀態(tài)。如果沒(méi)有調(diào)用任何的阻塞函數(shù)却汉,線程只會(huì)在RUNNING和READY之間切換驯妄,也就是系統(tǒng)的時(shí)間片調(diào)度。這兩種狀態(tài)的切換是操作系統(tǒng)完成的合砂,除非手動(dòng)調(diào)用yield()函數(shù)青扔,放棄對(duì)CPU的占用。
????????一旦調(diào)用了圖中的任何阻塞函數(shù)既穆,線程就會(huì)進(jìn)入WAITING或者TIMED_WAITING狀態(tài)赎懦,兩者的區(qū)別只是前者為無(wú)限期阻塞,后者則傳入了一個(gè)時(shí)間參數(shù)幻工,阻塞一個(gè)有限的時(shí)間励两。如果使用了synchronized關(guān)鍵字或者synchronized塊,則會(huì)進(jìn)入BLOCKED狀態(tài)囊颅。
????????不太常見(jiàn)的阻塞/喚醒函數(shù)当悔,LockSupport.park()/unpark()。這對(duì)函數(shù)非常關(guān)鍵踢代,Concurrent包中Lock的實(shí)現(xiàn)即依賴這一對(duì)操作原語(yǔ)盲憎。
????????因此thread.interrupted()的精確含義是“喚醒輕量級(jí)阻塞”,而不是字面意思“中斷一個(gè)線程”胳挎。
thread.isInterrupted()與Thread.interrupted()的區(qū)別
????????因?yàn)?thread.interrupted()相當(dāng)于給線程發(fā)送了一個(gè)喚醒的信號(hào)饼疙,所以如果線程此時(shí)恰好處于WAITING或者TIMED_WAITING狀態(tài),就會(huì)拋出一個(gè)InterruptedException慕爬,并且線程被喚醒窑眯。而如果線程此時(shí)并沒(méi)有被阻塞,則線程什么都不會(huì)做医窿。但在后續(xù)磅甩,線程可以判斷自己是否收到過(guò)其他線程發(fā)來(lái)的中斷信號(hào),然后做一些對(duì)應(yīng)的處理姥卢。
? ??????這兩個(gè)方法都是線程用來(lái)判斷自己是否收到過(guò)中斷信號(hào)的卷要,前者是實(shí)例方法渣聚,后者是靜態(tài)方法。二者的區(qū)別在于僧叉,前者只是讀取中斷狀態(tài)奕枝,不修改狀態(tài);后者不僅讀取中斷狀態(tài)瓶堕,還會(huì)重置中斷標(biāo)志位倍权。
1.5 線程的優(yōu)雅關(guān)閉
1.5.1 stop與destory函數(shù)
1.5.2 守護(hù)線程
daemon線程和非daemon線程的對(duì)比:
1.5.3 設(shè)置關(guān)閉的標(biāo)志位
開(kāi)發(fā)中一般通過(guò)設(shè)置標(biāo)志位的方式,停止循環(huán)運(yùn)行的線程捞烟。
????????但上面的代碼有一個(gè)問(wèn)題:如果MyThread t在while循環(huán)中阻塞在某個(gè)地方,例如里面調(diào)用了object.wait()函數(shù)当船,那它可能永遠(yuǎn)沒(méi)有機(jī)會(huì)再執(zhí)行 while( ! stopped)代碼题画,也就一直無(wú)法退出循環(huán)。
? ??????此時(shí)德频,就要用到InterruptedException()與interrupt()函數(shù)苍息。
2 并發(fā)核心概念
2.1 并發(fā)與并行
2.2 同步?
2.3 不可變對(duì)象
2.4 原子操作和原子變量
2.5 共享內(nèi)存與消息傳遞
3 并發(fā)的問(wèn)題
3.1 數(shù)據(jù)競(jìng)爭(zhēng)
????????如果有兩個(gè)或者多個(gè)任務(wù)在臨界段之外對(duì)一個(gè)共享變量進(jìn)行寫(xiě)入操作,也就是說(shuō)沒(méi)有使用任何同步機(jī)制壹置,那么應(yīng)用程序可能存在數(shù)據(jù)競(jìng)爭(zhēng)(也叫做競(jìng)爭(zhēng)條件)竞思。
????????在這些情況下,應(yīng)用程序的最終結(jié)果可能取決于任務(wù)的執(zhí)行順序钞护。
????????假設(shè)有兩個(gè)不同的任務(wù)執(zhí)行了同一個(gè)modify方法盖喷。由于任務(wù)中語(yǔ)句的執(zhí)行順序不同,最終結(jié)果也會(huì)不同难咕。
????????modify方法不是原子的课梳, ConcurrentDemo 也不是線程安全的。
3.2 死鎖
3.3 活鎖
3.4 資源不足
3.5 優(yōu)先權(quán)反轉(zhuǎn)