本筆記來自 計算機程序的思維邏輯 系列文章
線程
創(chuàng)建線程的方式
- 繼承
Thread
- 實現(xiàn)
Runnable
接口
屬性和方法
long tid
線程ID犀暑,遞增整數(shù)String name
線程名,默認(rèn)以 Thread- + 線程編號 構(gòu)成int priority
優(yōu)先級担扑,范圍是 1 到 10 等孵,默認(rèn)是 5int threadStatus
狀態(tài)了赵,枚舉類型:NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED
-
boolean daemon
是否守護線程當(dāng)程序中只存在
daemon
線程時脓匿,程序就會退出程序運行時遍略,除了創(chuàng)建
main
線程压鉴,至少還會創(chuàng)建一個負(fù)責(zé)垃圾回收的線程崖咨,它就是daemon
線程,當(dāng)main
線程結(jié)束時油吭,垃圾回收線程也會退出 boolean isAlive()
判斷線程是否存活void yield()
表示當(dāng)前線程不著急使用CPUvoid sleep(long millis)
讓當(dāng)前線程睡眠一段時間void join(long millis)
等待當(dāng)前線程击蹲,0 表示無限等待
特點
共享內(nèi)存
每個線程表示一條單獨的執(zhí)行流,有自己的程序計數(shù)器婉宰,有自己的棧歌豺,但線程之間可以共享內(nèi)存,它們可以訪問和操作相同的對象
競態(tài)條件
當(dāng)多個線程訪問和操作同一個對象時心包,最終結(jié)果與執(zhí)行時序有關(guān)类咧,可能正確也可能不正確
解決方法:使用synchronized
關(guān)鍵字,使用顯式鎖蟹腾,使用原子變量
內(nèi)存可見性
多個線程可以共享訪問和操作同一個變量痕惋,但一個線程對一個共享變量的修改,另一個線程不一定馬上就能看到娃殖,可能永遠(yuǎn)看不到
在計算機系統(tǒng)中值戳,除了內(nèi)存,數(shù)據(jù)還會被緩存在CPU的寄存器以及各級緩存中炉爆,當(dāng)訪問一個變量時堕虹,可能直接從寄存器或CPU緩存中獲取,而不一定到內(nèi)存中取芬首,當(dāng)修改一個變量時赴捞,也可能是先寫到緩存中,而稍后才會同步更新到內(nèi)存中
優(yōu)點
- 充分利用多CPU的計算能力郁稍,單線程只能利用一個CPU
- 充分利用硬件資源赦政,多個獨立的網(wǎng)絡(luò)請求,完全可以使用多個線程同時請求
- 在用戶界面應(yīng)用程序中艺晴,保持程序的響應(yīng)性昼钻,界面和后臺任務(wù)通常是不同的線程
- 簡化建模和IO處理
成本
- 創(chuàng)建線程需要消耗操作系統(tǒng)的資源掸屡,操作系統(tǒng)會為每個線程創(chuàng)建必要的數(shù)據(jù)結(jié)構(gòu)封寞、棧、程序計數(shù)器等仅财,創(chuàng)建需要時間
- 當(dāng)有大量可運行線程時狈究,操作系統(tǒng)會忙于調(diào)度,為一個線程分配一段時間盏求,執(zhí)行完后抖锥,再讓另一個線程執(zhí)行亿眠。切換出去時,系統(tǒng)需要保存線程當(dāng)前上下文狀態(tài)到內(nèi)存磅废;切換回來時纳像,需要恢復(fù)。這種切換會使CPU的緩存失效拯勉,而且耗時
- 創(chuàng)建超過CPU數(shù)量的線程是不必要的
synchronized
可以用于修飾類的實例方法竟趾、靜態(tài)方法和代碼塊
實例方法
- 保護同一對象的方法調(diào)用,實際保護的是當(dāng)前實例對象
- 對象有一個鎖和一個等待隊列宫峦,鎖只能被一個線程持有岔帽,當(dāng)前線程不能獲得鎖時,會加入等待隊列
- 保護的是對象而不是代碼导绷,只要訪問的是同一個對象的
synchronized
方法犀勒,即使是不同的方法,也會同步順序訪問
靜態(tài)方法
- 保護的是類對象
- 不同的兩個線程妥曲,可以同時一個執(zhí)行
synchronized
靜態(tài)方法贾费,另一個執(zhí)行synchronized
實例方法
代碼塊
- 在方法中使用
synchronized
,傳入一個對象檐盟,即保護對象 - 實例方法用
this
铸本,靜態(tài)方法則用Class
特點
可重入性
- 通過記錄鎖的持有線程和持有數(shù)量來實現(xiàn)
- 同個線程,在獲得鎖后遵堵,調(diào)用其它需要同樣鎖的代碼時箱玷,可以直接調(diào)用;即一個線程調(diào)用的一個
synchronized
實例方法內(nèi)可以直接調(diào)用其它synchronized
實例方法
內(nèi)存可見性
- 在釋放鎖時陌宿,所有寫入都會寫回內(nèi)存锡足,獲得鎖時,都會從內(nèi)存讀取最新數(shù)據(jù)壳坪。成本有點高
- 給變量加修飾符
volatile
舶得,保證讀寫到內(nèi)存最新值
死鎖
- a持有鎖A,等待鎖B爽蝴,b持有鎖B沐批,等待鎖A,陷入互相等待
- 應(yīng)該盡量避免在持有一個鎖的同時去申請另一個鎖蝎亚,如果確實需要多個鎖九孩,所有代碼都應(yīng)該按照相同順序去申請鎖
同步容器
通過給所有容器方法加上synchronized
來實現(xiàn)安全。如:SynchronizedCollection
但以下情況不是安全的
- 復(fù)合操作
- 偽同步
- 迭代
并發(fā)容器
同步容器性能比較低发框,Java有很多專門為并發(fā)設(shè)計的容器類躺彬。如:CopyOnWriteArrayList
ConcurrentHashMap
線程的基本協(xié)作機制
wait 等待
把當(dāng)前線程放到條件等待隊列并阻塞,等待喚醒
過程
- 把當(dāng)前線程放入條件等待隊列,釋放對象鎖宪拥,阻塞等待仿野,線程狀態(tài)變?yōu)?WAITING 或 TIMED_WAITING
- 等待時間到或被其它線程調(diào)用
notify
/notifyAll
從條件等待隊列中移除,這時她君,要重新競爭對象鎖- 如果能夠獲得鎖脚作,線程狀態(tài)變?yōu)?RUNNABLE ,并從
wait
調(diào)用中返回 - 否則缔刹,該線程加入對象鎖等待隊列鳖枕,線程狀態(tài)變?yōu)?BLOCKED ,只有在獲得鎖后才會從
wait
調(diào)用中返回
- 如果能夠獲得鎖脚作,線程狀態(tài)變?yōu)?RUNNABLE ,并從
線程從wait
調(diào)用中返回后桨螺,不代表其等待的條件就一定成立了宾符,它需要重新檢查其等待的條件。
一般調(diào)用模式
synchronized (obj) {
while (條件不成立) {
obj.wait();
}
// ... 執(zhí)行條件滿足后的操作
}
notify 喚醒
從條件等待隊列中移除一個線程并將之喚醒
notifyAll 喚醒
移除條件等待隊列中所有線程并全部喚醒
機制
- 每個對象都有一把鎖和用于鎖的等待隊列灭翔,還有一個條件等待隊列魏烫,用于線程間的協(xié)作
- 一個對象調(diào)用
wait
方法就會當(dāng)前線程放到條件隊列上并阻塞,表示當(dāng)前線程執(zhí)行不下去了肝箱,需要等待一個條件哄褒,這個條件它本身無法改變,需要其它線程改變 - 當(dāng)其它線程改變了條件后煌张,調(diào)用該對象的
notify
或notifyAll
方法呐赡,將其喚醒
注意
-
wait
和notify
方法只能在synchronized
代碼塊內(nèi)被調(diào)用,如果調(diào)用時骏融,當(dāng)前線程沒有持有對象鎖链嘀,會拋異常IllegalMonitorStateException
- 雖然是在
synchronized
方法內(nèi),但調(diào)用wait
時档玻,線程會釋放鎖 -
notify
方法不會釋放鎖
協(xié)作的核心
共享的條件變量
場景
- 同時開始 共享同一個條件變量
-
等待結(jié)束
CountDownLatch
-
異步結(jié)果
Executor
Future
-
集合點
CyclicBarrier
線程的中斷
取消/關(guān)閉的機制
在Java中怀泊,停止一個線程的主要機制是中斷,中斷并不是強迫終止一個線程误趴,它是一種協(xié)作機制霹琼,是給線程傳遞一個取消信號,但是由線程來決定如何及何時退出
每個線程都有一個標(biāo)志位凉当,表示該線程是否被中斷了
-
void stop()
已過時 -
boolean isInterrupted()
返回對應(yīng)線程的中斷標(biāo)志位 -
void interrupt()
中斷對應(yīng)的線程 -
static boolean interrupted()
返回當(dāng)前線程的中斷標(biāo)志位枣申;同時清空中斷標(biāo)志位
線程對中斷的反應(yīng)
interrupt
對線程的影響與線程的狀態(tài)和在進行的IO操作有關(guān)
RUNNABLE
線程在運行或具備運行條件只是在等待操作系統(tǒng)調(diào)度
- 如果線程在運行中,且沒有執(zhí)行IO操作看杭,
interrupt
只是會設(shè)置線程的中斷標(biāo)志位忠藤,沒有任何其它作用;線程應(yīng)該在運行過程中合適的位置檢查中斷標(biāo)志位
WAITING / TIMED_WAITING
線程在等待某個條件或超時
- 線程執(zhí)行
join()
或wait()
會進入 WAITING 狀態(tài) - 線程執(zhí)行
wait(long timeout)
sleep(long millis)
或join(long millis)
會進入 TIMED_WAITING 狀態(tài) - 在這些狀態(tài)時泊窘,調(diào)用
interrupt
會使線程拋異常InterruptedException
熄驼,拋異常后,中斷標(biāo)志位被清空 - 捕獲到
InterruptedException
烘豹,通常希望結(jié)束該線程瓜贾,有兩種處理方式- 向上傳遞該異常,使得該方法也變成了一個可中斷的方法携悯,需要調(diào)用者進行處理
- 不能向上傳遞時祭芦,捕獲異常,進行合適的清理操作憔鬼,清理后龟劲,一般調(diào)用
interrupt
方法設(shè)置中斷標(biāo)志位,使其它代碼知道它發(fā)生了中斷
BLOCKED
線程在等待鎖轴或,試圖進入同步塊
- 如果線程在等待鎖昌跌,調(diào)用
interrupt
只會設(shè)置線程的中斷標(biāo)志位,線程依然處于 BLOCKED 狀態(tài)
NEW / TERMINATE
線程還沒啟動或已結(jié)束
- 在這些狀態(tài)時照雁,調(diào)用
interrupt
對它沒有任何效果蚕愤,中斷標(biāo)志位也不會被設(shè)置
IO操作
- 如果IO通道是可中斷的,即實現(xiàn)了
InterruptibleChannel
接口饺蚊,則IO操作關(guān)閉萍诱,線程的中斷標(biāo)志位會被設(shè)置,同時線程會收到異常ClosedByInterruptException
- 如果線程阻塞于
Selector
調(diào)用污呼,則線程的中斷標(biāo)志位會被設(shè)置裕坊,同時阻塞的調(diào)用會立即返回
正確取消/關(guān)閉線程
-
interrupt
方法不會真正中斷線程,只是一種協(xié)作機制 - 以線程提供服務(wù)的程序模塊燕酷,應(yīng)該封裝取消/關(guān)閉操作籍凝,提供單獨的取消/關(guān)閉方法給調(diào)用者,而不是直接調(diào)用
interrupt
方法
原子變量和CAS
包含一些以原子方式實現(xiàn)組合操作的方法
基本原子變量類型
- AtomicInteger
- AtomicBoolean
- AtomicLong
- AtomicReference苗缩,AtomicMarkableReference静浴,AtomicStampedReference
數(shù)組類型
- AtomicIntegerArray
- AtomicLongArray
- AtomicReferenceArray
更新類
- AtomicIntegerFieldUpdater
- AtomicLongFieldUpdater
- AtomicReferenceFieldUpdater
內(nèi)部實現(xiàn)
依賴compareAndSet
方法,簡稱 CAS
CAS 是Java并發(fā)包的基礎(chǔ)挤渐,基于它可以實現(xiàn)高效苹享、樂觀、非阻塞式的數(shù)據(jù)結(jié)構(gòu)和算法浴麻,也是并發(fā)包中鎖得问、同步工具和各種容器的基礎(chǔ)
對比
-
synchronized
是悲觀的,它假定更新很可能沖突软免,所以先獲得鎖宫纬,得到鎖后才更新;原子變量
是樂觀的膏萧,它假定沖突比較少漓骚,但使用CAS更新蝌衔,也就是進行沖突檢測,如果沖突蝌蹂,就繼續(xù)嘗試 -
synchronized
是阻塞式算法噩斟,得不到鎖時,進入鎖等待隊列孤个,等待其它線程喚醒剃允,有上下文切換開銷;而原子變量
是非阻塞式的齐鲤,更新沖突時斥废,就重試,不會阻塞给郊,不會有上下文切換開銷
顯式鎖
支持以非阻塞方式獲取鎖牡肉,可以響應(yīng)中斷,可以限時
接口 Lock
-
void lock()
獲取鎖淆九,會阻塞直到成功 -
void unlock()
釋放鎖 -
void lockInterruptibly()
可以響應(yīng)中斷荚板,被其它線程中斷時,拋出InterruptedException
異常 -
boolean tryLock()
只是嘗試獲取鎖吩屹,立即返回跪另,不阻塞;如果獲取成功煤搜,返回 true 免绿,否則返回 false -
boolean tryLock(long time, TimeUnit unit)
先嘗試獲取鎖,如果成功則立即返回 true 擦盾,否則阻塞等待嘲驾,等待最長時間為指定的參數(shù),在等待的同時響應(yīng)中斷迹卢,如果發(fā)生中斷裕循,拋出InterruptedException
異常蟋恬,如果在等待時間內(nèi)獲得鎖球恤,返回 true 蛹疯,否則返回 false -
Condition newCondition()
新建一個條件,一個Lock
可以關(guān)聯(lián)多個條件
可重入鎖 ReentrantLock
基本用法
該類的lock
和unlock
方法實現(xiàn)了與synchronized
一樣的語義
- 可重入症见,即一個線程在持有一個鎖的前提下喂走,可以繼續(xù)獲得該鎖
- 可以解決競態(tài)條件問題
- 可以保證內(nèi)存可見性
帶參數(shù)boolean fair
的構(gòu)造方法
- 參數(shù) fair 表示是否保證公平,不指定的情況下谋作,默認(rèn)為 false 芋肠,表示不保證公平
- 公平指等待最長時間的線程優(yōu)先獲得鎖;保證公平會影響性能遵蚜,一般不需要
-
synchronized
鎖也是不保證公平的
使用顯式鎖帖池,一定要記得調(diào)用unlock
奈惑,一般而言,應(yīng)該將lock
之后的代碼包裝到try
語句內(nèi)睡汹,在finally
語句內(nèi)釋放鎖
使用tryLock避免死鎖
在持有一個鎖肴甸,獲取另一個鎖,獲取不到的時候帮孔,可以釋放已持有的鎖雷滋,給其它線程機會獲取鎖不撑,然后再重試獲取所有鎖
獲取鎖信息
用于監(jiān)控和調(diào)試
-
boolean isLocked()
是否被持有文兢,不一定是當(dāng)前線程持有 -
int getHoldCount()
鎖被當(dāng)前線程持有的數(shù)量;0 表示不被當(dāng)前線程持有 -
boolean isHeldByCurrentThread()
是否被當(dāng)前線程持有 -
boolean isFair()
鎖等待策略是否公平 -
boolean hasQueuedThreads()
是否有線程在等待該鎖 -
boolean hasQueuedThread(Thread thread)
指定的線程是否在等待該鎖 -
int getQueueLength()
在等待該鎖的線程個數(shù)
實現(xiàn)原理
依賴 CAS 和 LockSupport
類
LockSupport
-
void park()
使當(dāng)前線程放棄CPU焕檬,進入等待狀態(tài)姆坚,操作系統(tǒng)不再對它進行調(diào)度,直到有其它線程對它調(diào)用了unpark
-
void parkNanos(long nanos)
指定等待的最長時間实愚,相對時間 -
void parkUntil(long deadline)
指定最長等到什么時候兼呵,絕對時間 -
void unpark(Thread thread)
使線程恢復(fù)可運行狀態(tài)
顯式條件
顯式條件與wait
/notify
相對應(yīng)
wait
/notify
與synchronized
配合使用;顯式條件與顯式鎖配合使用
Condition
通過顯式鎖創(chuàng)建 Condition newCondition()
void await()
對應(yīng)于wait()
void signal()
對應(yīng)于notify()
void signalAll()
對應(yīng)于notifyAll()
-
boolean await(long time, TimeUnit unit)
指定等待的時間腊敲,相對時間如果等待超時击喂,返回 false ,否則為 true
-
long awaitNanos(long nanosTimeout)
指定等待的時間碰辅,相對時間返回值是 nanosTimeout 減去實際等待的時間
-
boolean awaitUntil(Date deadline)
指定最長等到什么時候懂昂,絕對時間如果等待超時,返回 false 没宾,否則返回 true
-
void awaitUninterruptibly()
不響應(yīng)中斷的等待如果等待過程發(fā)生了中斷凌彬,中斷標(biāo)志位會被設(shè)置
機制
- 和
wait
方法一樣,調(diào)用await
方法前需要先獲取鎖循衰,如果沒有鎖铲敛,會拋IllegalMonitorStateException
異常 -
await
在進入等待隊列后,會釋放鎖会钝,釋放CPU - 當(dāng)其它線程將它喚醒后伐蒋,或等待超時后,或發(fā)生中斷異常后迁酸,它都需要重新獲取鎖咽弦,獲取鎖后,才會從
await
方法中退出 -
await
返回后胁出,不代表其等待的條件就一定滿足了型型,通常要將await
的調(diào)用放到一個循環(huán)內(nèi),只有條件滿足后才退出
并發(fā)容器
CopyOnWriteArrayList
區(qū)別
- 線程安全全蝶,可以被多個線程并發(fā)訪問
- 它的迭代器不支持修改操作闹蒜,但也不會拋出
ConcurrentModificationException
異常 - 它以原子方式支持一些復(fù)合操作
原理
寫時拷貝
- 內(nèi)部是一個數(shù)組寺枉,但這個數(shù)組是以原子方式被整體更新的
- 每次修改操作,都會新建一個數(shù)組绷落,復(fù)制原數(shù)組的內(nèi)容到新數(shù)組姥闪,在新數(shù)組上進行需要的修改,然后以原子方式設(shè)置內(nèi)部數(shù)據(jù)的引用
保證線程安全的思路
- 使用鎖
- 循環(huán)CAS
- 寫時拷貝
CopyOnWriteArraySet
基于 CopyOnWriteArrayList
實現(xiàn)
ConcurrentHashMap
區(qū)別
- 并發(fā)安全
- 直接支持一些原子復(fù)合操作
- 支持高并發(fā)砌烁、讀操作完全并行筐喳、寫操作支持一定程度的并行
- 迭代不用加鎖,不會拋出
ConcurrentModificationException
異常 - 弱一致性
原子復(fù)合操作
實現(xiàn)了ConcurrentMap
接口
-
V putIfAbsent(K key, V value)
條件更新如果 key 不存在函喉,則設(shè)置 key 為 value 避归,返回之前的值
如果 key 存在,返回對應(yīng)的值
-
boolean remove(Object key, Object value)
條件刪除如果 key 存在且對應(yīng)值為 value 管呵,則刪除并返回 true 梳毙,否則返回 false
-
boolean replace(K key, V oldValue, V newValue)
條件替換如果 key 存在且對應(yīng)值為 oldValue ,則替換為 newValue 并返回 true 捐下,否則返回 false
-
V replace(K key, V value)
條件替換如果 key 存在账锹,則替換值為 value 并返回之前的值,否則返回 null
原理
- 分段鎖 將數(shù)據(jù)分為多個段坷襟,而每個段有一個獨立的鎖奸柬;每個段相當(dāng)于一個獨立的哈希表,分段的依據(jù)也是哈希值婴程,無論是保存鍵值對還是根據(jù)鍵查找廓奕,都先根據(jù)鍵的哈希值映射到段,再在段對應(yīng)的哈希表上進行操作
- 讀不需要鎖
弱一致性
迭代器創(chuàng)建后排抬,按照哈希表結(jié)構(gòu)遍歷每個元素懂从,但在遍歷過程中,內(nèi)部元素可能會發(fā)生變化蹲蒲,如果變化發(fā)生在已遍歷過的部分番甩,迭代器就不會反應(yīng)出來,如果變化發(fā)生在未遍歷的部分届搁,迭代器就會發(fā)現(xiàn)并反映出來
ConcurrentSkipListMap
基于 SkipList
跳躍表 實現(xiàn)
特點
- 沒有使用鎖缘薛,所有操作都是無阻塞的,所有操作都可以并行卡睦,多個線程可以同時寫
- 弱一致性宴胧,有些操作不是原子的
- 實現(xiàn)了
ConcurrentMap
接口,直接支持一些原子復(fù)合操作 - 實現(xiàn)了
SortedMap
和NavigableMap
接口表锻,可排序恕齐,默認(rèn)按鍵有序,可傳遞比較器自定義排序
跳表
基于 鏈表 瞬逊,在鏈表的基礎(chǔ)上加了多層索引結(jié)構(gòu)
高層的索引節(jié)點一定同時是低層的索引節(jié)點显歧;高層的索引節(jié)點少仪或,低層的多
每個索引節(jié)點,有兩個指針士骤,一個向右范删,指向下一個同層的索引節(jié)點,另一個向下拷肌,指向下一層的索引節(jié)點或基本鏈表節(jié)點
ConcurrentSkipListSet
基于 ConcurrentSkipListMap
實現(xiàn)
各種隊列
無鎖非阻塞并發(fā)隊列
都是基于鏈表實現(xiàn)到旦,沒有限制大小,無界巨缘;適用于多個線程并發(fā)使用一個隊列的場合
- ConcurrentLinkedQueue
- ConcurrentLinkedDeque
普通阻塞隊列
都實現(xiàn)了BlockingQueue
接口添忘,內(nèi)部使用顯式鎖ReentrantLock
和顯式條件Condition
- ArrayBlockingQueue 基于循環(huán)數(shù)組實現(xiàn),有界带猴,創(chuàng)建時指定大小昔汉,運行過程中不會改變
- LinkedBlockingQueue LinkedBlockingDeque 基于鏈表實現(xiàn)懈万,默認(rèn)無界
優(yōu)先級阻塞隊列
PriorityBlockingQueue 按優(yōu)先級出隊拴清,優(yōu)先級高的先出,無界
延時阻塞隊列
DelayQueue
- 特殊的優(yōu)先級隊列会通,無界
- 要求每個元素都實現(xiàn)
Delayed
接口 - 按元素的延時時間出隊口予,只有當(dāng)元素的延時過期之后才能從隊列中被拿走
其它阻塞隊列
- SynchronousQueue 入隊操作要等待另一個線程的出隊操作,反之亦然
- LinkedTransferQueue 入隊操作可以等待出隊操作后再返回
異步任務(wù)執(zhí)行服務(wù)
執(zhí)行服務(wù)
線程Thread
既表示要執(zhí)行的任務(wù)涕侈,又表示執(zhí)行的機制
執(zhí)行服務(wù)
將 任務(wù)的提交 和 任務(wù)的執(zhí)行 相分離沪停,封裝了任務(wù)執(zhí)行的細(xì)節(jié);對任務(wù)的提交者而言裳涛,它可以關(guān)注于任務(wù)本身木张,如提交任務(wù)、獲取結(jié)果端三、取消任務(wù)舷礼;而不需要關(guān)注任務(wù)執(zhí)行的細(xì)節(jié),如線程創(chuàng)建郊闯、任務(wù)調(diào)度妻献、線程關(guān)閉
基本接口
Runnable
表示要執(zhí)行的異步任務(wù),沒有返回結(jié)果团赁,不會拋異常
Callable
同樣指要執(zhí)行的異步任務(wù)育拨,有返回結(jié)果,會拋異常
Executor
表示執(zhí)行服務(wù)欢摄,執(zhí)行一個Runnable
熬丧,沒有返回結(jié)果
ExecutorService
擴展了Executor
,定義了更多服務(wù)
submit
方法都表示提交任務(wù)怀挠,返回后析蝴,只是表示任務(wù)已提交矗钟,不代表已執(zhí)行-
void shutdown()
關(guān)閉不再接收新任務(wù),但已提交的任務(wù)會繼續(xù)執(zhí)行嫌变,即使任務(wù)還未開始執(zhí)行
-
List<Runnable> shutdownNow()
關(guān)閉不再接收新任務(wù)吨艇,已提交但尚未執(zhí)行的任務(wù)會被終止,并嘗試中斷正在執(zhí)行的任務(wù)
返回已提交但尚未執(zhí)行的任務(wù)列表
boolean isShutdown()
是否調(diào)用了shutdown
或shutdownNow
-
boolean isTerminated()
是否所有任務(wù)都已結(jié)束只有調(diào)用過
shutdown
或shutdownNow
并且所有任務(wù)都已結(jié)束才返回 true 腾啥,否則返回 false -
boolean awaitTermination(long timeout, TimeUnit unit)
限定等待時間东涡,等待所有任務(wù)結(jié)束當(dāng)所有任務(wù)都結(jié)束,返回 true
等待超時倘待,返回 false
等待過程發(fā)生中斷疮跑,會拋
InterruptedException
異常 -
invokeAll
等待所有任務(wù)完成可以指定等待時間,如果超時后有任務(wù)還沒完成凸舵,就會被取消
-
invokeAny
只要有一個任務(wù)完成祖娘,則返回該任務(wù)的結(jié)果,其它任務(wù)會被取消可以指定等待時間啊奄,如果超時沒有任務(wù)完成渐苏,會拋
TimeoutException
異常如果所有任務(wù)都發(fā)生異常,則拋
ExecutionException
異常
Future
表示異步任務(wù)的執(zhí)行結(jié)果
-
boolean cancel(boolean mayInterruptIfRunning)
取消異步任務(wù)如果任務(wù)已完成菇夸、或已經(jīng)取消琼富、或由于某種原因不能取消,返回 false 庄新,否則返回 true
如果任務(wù)還未開始鞠眉,則不再運行
參數(shù)
mayInterruptIfRunning
表示任務(wù)正在執(zhí)行,是否調(diào)用interrupt
方法中斷線程 -
boolean isCancelled()
任務(wù)是否被取消只要
cancel
方法返回 true 择诈,隨后此方法都會返回 true -
boolean isDone()
任務(wù)是否結(jié)束任務(wù)正常結(jié)束械蹋、任務(wù)拋異常、任務(wù)被取消羞芍,都視為結(jié)束
-
V get()
返回異步任務(wù)最終的結(jié)果如果任務(wù)還未執(zhí)行完成哗戈,會阻塞等待
V get(long timeout, TimeUnit unit)
同上,限定了阻塞等待的時間涩金,如果超時任務(wù)還未結(jié)束谱醇,會拋TimeoutException
異常
任務(wù)結(jié)果
- 正常完成
- 異常 將原異常包裝為
ExecutionException
重新拋出 - 取消 拋出
CancellationException
異常
線程池
概念
主要由 任務(wù)隊列 和 工作者線程 組成
工作者線程主體是一個循環(huán),循環(huán)從隊列中接受任務(wù)并執(zhí)行步做,任務(wù)隊列保存待執(zhí)行的任務(wù)
優(yōu)點
- 可以重用線程副渴,避免線程創(chuàng)建的開銷
- 在任務(wù)過多時,通過排隊避免創(chuàng)建過多線程全度,減少系統(tǒng)資源消耗和競爭煮剧,確保任務(wù)有序完成
構(gòu)造方法參數(shù)
corePoolSize
核心線程個數(shù)
有新任務(wù)時,如果當(dāng)前線程數(shù)小于核心線程個數(shù),就會創(chuàng)建一個新線程來執(zhí)行該任務(wù)勉盅,即使其它線程是空閑的佑颇,也會創(chuàng)建新線程
如果線程個數(shù)大于等于核心線程個數(shù),就不會立即創(chuàng)建新線程草娜,會先嘗試排隊挑胸,如果隊列滿了或其它原因不能立即入隊,就檢查線程個數(shù)是否達(dá)到最大線程個數(shù)宰闰,如果沒有茬贵,就繼續(xù)創(chuàng)建線程,直到線程數(shù)達(dá)到最大線程個數(shù)
maximumPoolSize
最大線程個數(shù)
不管創(chuàng)建多少任務(wù)移袍,都不會創(chuàng)建比這個值大的線程個數(shù)
keepAliveTime unit
空閑線程存活時間
目的是為了釋放多余的線程資源
表示當(dāng)線程池中的線程個數(shù)大于核心線程個數(shù)時解藻,額外空閑線程的存活時間
workQueue
隊列,要求是阻塞隊列BlockingQueue
如果是無界隊列葡盗,線程個數(shù)最多達(dá)到核心線程個數(shù)螟左,到達(dá)后,新任務(wù)總會排隊
handler
任務(wù)拒絕策略
如果隊列有界觅够,且最大線程個數(shù)有限胶背,當(dāng)隊列滿,線程個數(shù)也達(dá)到最大線程個數(shù)時蔚约,觸發(fā)線程池的任務(wù)拒絕策略
四種處理方式
-
AbortPolicy
默認(rèn)處理奄妨,拋出RejectedExecutionException
異常 -
DiscardPolicy
靜默處理涂籽,忽略新任務(wù)苹祟,不拋異常,也不執(zhí)行 -
DiscardOldestPolicy
將等待時間最長的任務(wù)扔掉评雌,自己排隊 -
CallerRunsPolicy
在任務(wù)提交者線程中執(zhí)行任務(wù)树枫,而不是交給線程池中的線程執(zhí)行
threadFactory
線程工廠
根據(jù)Runnable
創(chuàng)建一個Thread
Executors
工廠類,方便創(chuàng)建一些預(yù)配置的線程池
newSingleThreadExecutor
只使用一個線程景东,使用無界隊列砂轻,線程創(chuàng)建后不會超時終止,該線程順序執(zhí)行所有任務(wù)
適用于需要確保所有任務(wù)被順序執(zhí)行的場合
newFixedThreadPool
使用固定個數(shù)的線程斤吐,使用無界隊列搔涝,線程創(chuàng)建后不會超時終止
如果排隊任務(wù)過多,可能會消耗非常大的內(nèi)存
newCachedThreadPool
當(dāng)有新任務(wù)時和措,如果正好有空閑線程庄呈,則接受任務(wù),否則總是創(chuàng)建一個新線程派阱,總線程個數(shù)不受限制
對任一空閑線程诬留,如果60秒內(nèi)沒有新任務(wù),就終止
線程池的死鎖
任務(wù)之間有依賴,可能會出現(xiàn)死鎖
任務(wù)A在執(zhí)行過程中文兑,提交了任務(wù)B盒刚,需等待任務(wù)B結(jié)束,如果任務(wù)A提交給一個單線程線程池绿贞,或者任務(wù)B由于線程占滿需要排隊等待因块,那么就會出現(xiàn)死鎖,A在等待B的結(jié)果籍铁,而B在隊列中等待調(diào)度
CompletionService
場景
主線程提交多個異步任務(wù)贮聂,有任務(wù)完成就處理結(jié)果,并且按任務(wù)完成順序逐個處理
方法
-
Future<V> take()
獲取下一個完成任務(wù)的結(jié)果寨辩,阻塞等待 -
Future<V> poll()
獲取下一個完成任務(wù)的結(jié)果吓懈,立即返回;如果沒有已經(jīng)完成的任務(wù)靡狞,返回 null -
Future<V> poll(long timeout, TimeUnit unit)
獲取下一個完成任務(wù)的結(jié)果耻警,限定等待時間
實現(xiàn)原理
- 依賴
Executor
完成實際的任務(wù)提交,自己負(fù)責(zé)結(jié)果的排隊和處理 - 內(nèi)部維護一個隊列甸怕,用來保存任務(wù)結(jié)果
實現(xiàn)類
ExecutorCompletionService
定時任務(wù)
Timer 和 TimerTask
TimerTask
表示定時任務(wù)甘穿,實現(xiàn)Runnable
接口
Timer
負(fù)責(zé)定時任務(wù)的調(diào)度和執(zhí)行
void schedule(TimerTask task, long delay)
當(dāng)前時間延時 delay 毫秒后執(zhí)行任務(wù)void schedule(TimerTask task, Date time)
再指定絕對時間 time 執(zhí)行任務(wù)-
void schedule(TimerTask task, long delay, long period)
固定延時重復(fù)執(zhí)行第一次執(zhí)行時間為當(dāng)前時間加上 delay ,后一次的計劃執(zhí)行時間為前一次 實際 執(zhí)行時間加上 period
-
void schedule(TimerTask task, Date firstTime, long period)
固定延時重復(fù)執(zhí)行第一次執(zhí)行時間為 firstTime 梢杭,后一次的計劃執(zhí)行時間為前一次 實際 執(zhí)行時間加上 period
如果 firstTime 是一個過去的時間温兼,任務(wù)會立即執(zhí)行
-
void scheduleAtFixedRate(TimerTask task, long delay, long period)
固定頻率執(zhí)行第一次執(zhí)行時間為當(dāng)前時間加上 delay ,后一次的計劃執(zhí)行時間為前一次 計劃 執(zhí)行時間加上 period
-
void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
固定頻率執(zhí)行第一次執(zhí)行時間為 firstTime 武契,后一次的計劃執(zhí)行時間為前一次 計劃 執(zhí)行時間加上 period
如果 firstTime 是一個過去的時間募判,任務(wù)會立即執(zhí)行
如果 firstTime 加上 period 還是一個過去時間,會連續(xù)運行多次咒唆,直到時間超過當(dāng)前時間
基本原理
Timer
內(nèi)部主要由 TaskQueue 和 TimerThread 兩部分組成
TaskQueue 是一個基于堆實現(xiàn)的優(yōu)先級隊列届垫,按照下次執(zhí)行的時間排優(yōu)先級
TimerThread 負(fù)責(zé)執(zhí)行所有的定時任務(wù),一個Timer
對象只有一個TimerThread
TimerThread 主體是一個循環(huán)全释,從隊列中拿任務(wù)装处,如果隊列中有任務(wù)且計劃執(zhí)行時間小于等于當(dāng)前時間,就執(zhí)行它浸船;如果隊列中沒有任務(wù)或第一個任務(wù)延時還沒到妄迁,就睡眠;如果睡眠過程中添加了新任務(wù)且新任務(wù)是第一個任務(wù)李命,該線程會被喚醒登淘,重新進行檢查
執(zhí)行任務(wù)之前,該線程判斷任務(wù)是否為周期任務(wù)项戴,如果是形帮,就設(shè)置下次執(zhí)行的時間并添加到優(yōu)先級隊列中槽惫;對于固定延時的任務(wù),下次執(zhí)行時間為當(dāng)前時間加上 period 辩撑;對于固定頻率的任務(wù)界斜,下次執(zhí)行時間為上次計劃執(zhí)行時間加上 period
注意的是,下次任務(wù)的計劃是在執(zhí)行當(dāng)前任務(wù)之前就做出的合冀。
對于固定延時任務(wù)各薇,延時相對的是任務(wù)執(zhí)行前的當(dāng)前時間,而不是任務(wù)執(zhí)行后的
注意
-
死循環(huán)
一個
Timer
對象只有一個線程君躺,意味著峭判,定時任務(wù)不能耗時太長,更不能是無限循環(huán) -
異常處理
在執(zhí)行任何一個任務(wù)的
run
方法時棕叫,一旦拋出異常林螃,TimerThread
就會退出,從而所有定時任務(wù)都會被取消為了各任務(wù)互不干擾俺泣,在
run
方法內(nèi)捕獲所有異常
ScheduledExecutorService
特點
- 基于線程池實現(xiàn)疗认,可以有多個線程
- 對于周期任務(wù),在任務(wù)執(zhí)行后再設(shè)置下次執(zhí)行的時間
- 任務(wù)執(zhí)行線程會捕獲任務(wù)執(zhí)行過程中的所有異常伏钠,一個定時任務(wù)的異常不會影響其它定時任務(wù)横漏,發(fā)生異常的任務(wù)會被取消
方法
-
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
延時 delay 后單次執(zhí)行
-
<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
延時 delay 后單次執(zhí)行
-
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
固定頻率,重復(fù)執(zhí)行
第一次執(zhí)行時間為 initialDelay 后熟掂,第二次為 initialDelay + period 缎浇,依此類推
-
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
固定延時,重復(fù)執(zhí)行
對于固定延時任務(wù)赴肚,延時相對的是任務(wù)執(zhí)行后的當(dāng)前時間
并發(fā)同步協(xié)作工具
ReentrantReadWriteLock
可重入讀寫鎖
特點
一個讀鎖素跺,一個寫鎖;讀操作使用讀鎖尊蚁,寫操作使用寫鎖
只有 讀 - 讀 操作可以并行亡笑,讀 - 寫 和 寫 - 寫 都不可以并行
只有一個線程可以進行 寫 操作,在獲取寫鎖時横朋,只有沒有任何線程持有任何鎖才可以獲取到;在持有寫鎖時百拓,其它任何線程都獲取不到任何鎖
在沒有其它線程持有寫鎖的情況下琴锭,多個線程可以獲取和持有讀鎖
Semaphore
信號量
特點
限制對資源的并發(fā)訪問數(shù)
一般鎖只能由持有鎖的線程釋放,而Semaphore
表示的只是一個許可數(shù)衙传,任意線程都可以調(diào)用其 release 方法
方法
void acquire()
獲取許可决帖,阻塞等待void acquireUninterruptibly()
獲取許可,阻塞等待蓖捶,不響應(yīng)中斷void acquire(int permits)
批量獲取多個許可void acquireUninterruptibly(int permits)
批量獲取多個許可-
boolean tryAcquire()
嘗試獲取許可地回,立即返回當(dāng)前有可用許可,返回 true ,否則返回 false
boolean tryAcquire(long timeout, TimeUnit unit)
嘗試獲取許可刻像,限定等待時間void release()
釋放許可
CountDownLatch
倒計時門栓
特點
一開始門栓關(guān)閉畅买,所有線程都需要等待,然后開始倒計時细睡,倒計時為 0 后谷羞,門栓打開,等待的所有線程都可以通過
一次性溜徙,打開后就不能再關(guān)上了
參與線程有不同角色湃缎,有的負(fù)責(zé)倒計時,有的在等待倒計時蠢壹;負(fù)責(zé)倒計時和等待倒計時的線程都可以有多個嗓违,用于不同角色線程之間的同步
場景
- 同時開始
- 主從協(xié)作
CyclicBarrier
循環(huán)柵欄
特點
適用于并行迭代計算,每個線程負(fù)責(zé)一部分計算图贸,然后在柵欄處等待其它線程完成靠瞎,所有線程到齊后,交換數(shù)據(jù)和計算結(jié)果求妹,再進行下一次迭代
可以重復(fù)利用
參與線程角色是一樣的乏盐,用于同一角色線程間的協(xié)調(diào)一致
小結(jié)
- 在讀多寫少的場景中使用
ReentrantReadWriteLock
替代ReentrantLock
,以提高性能 - 使用
Semaphore
限制對資源的并發(fā)訪問數(shù) - 使用
CountDownLatch
實現(xiàn)不同角色線程間的同步 - 使用
CyclicBarrier
實現(xiàn)同一角色線程間的協(xié)調(diào)一致
ThreadLocal
每個線程都有同一個變量的獨有拷貝
不同線程訪問的雖然是同一個變量制恍,但每個線程都有自己的獨立的值
方法
T get()
獲取值void set(T value)
設(shè)置值T initialValue()
提供初始值父能,默認(rèn)實現(xiàn)是返回 nullT setInitialValue()
設(shè)置初始值,返回之前的初始值-
void remove()
刪除當(dāng)前線程對應(yīng)的值刪除后净神,當(dāng)再次調(diào)用
get
方法時何吝,返回初始值
實現(xiàn)原理
每個線程都有一個 Map ,對于每個ThreadLocal
對象鹃唯,調(diào)用其get
set
方法爱榕,實際上就是以ThreadLocal
對象為鍵讀寫當(dāng)前線程的 Map
小結(jié)
ThreadLocal
常用于存儲上下文信息,避免在不同代碼間來回傳遞坡慌,簡化代碼
ThreadLocal
是實現(xiàn)線程安全黔酥、減少競爭的一種方案
在線程池中使用ThreadLocal
,需要確保初始值是符合期望的
并發(fā)總結(jié)
線程安全的機制
線程表示一條單獨的執(zhí)行流洪橘,每個線程都有自己的執(zhí)行計數(shù)器跪者,有自己的棧,但可以共享內(nèi)存
共享內(nèi)存是實現(xiàn)線程協(xié)作的基礎(chǔ)
共享內(nèi)存
兩個問題
- 競態(tài)條件
- 內(nèi)存可見性
解決方法
- 使用
synchronized
- 使用顯式鎖
- 使用
volatile
- 使用原子變量和CAS
- 寫時拷貝
- 使用
ThreadLocal
線程的協(xié)作機制
協(xié)作場景
- 生產(chǎn)者/消費者協(xié)作模式
- 主從協(xié)作模式
- 同時開始
- 集合點
機制
-
wait
/notify
- 顯式條件
- 線程的中斷
- 協(xié)作工具類
- 阻塞隊列
-
Future
/FutureTask
容器類
同步容器
基于普通容器返回線程安全的同步容器熄求,如SynchronizedList
并發(fā)容器
- 寫時拷貝的
CopyOnWriteArrayList
和CopyOnWriteArraySet
ConcurrentHashMap
- 基于
SkipList
的ConcurrentSkipListMap
和ConcurrentSkipListSet
- 各種隊列渣玲,如
ConcurrentLinkedQueue
,BlockingQueue
任務(wù)執(zhí)行服務(wù)
任務(wù)的提交 和 任務(wù)的執(zhí)行 相分離
實現(xiàn)機制:線程池
CompletionService
定時任務(wù)