相關(guān)源碼:boy-learning-netty
個(gè)人博客:http://bruce.bugmakers.club
內(nèi)容來(lái)自《極客時(shí)間 - Netty源碼剖析與實(shí)戰(zhàn)》
在程序開(kāi)發(fā)中霞篡,我們經(jīng)常用多線程技術(shù)來(lái)提高程序的工作效率步做,但是高并發(fā)的場(chǎng)景下,多線程會(huì)存在線程安全問(wèn)題碉克,用鎖來(lái)解決靶壮。
內(nèi)容一覽:
分析同步問(wèn)題的核心三要素
鎖的分類
-
Netty 玩轉(zhuǎn)鎖的五個(gè)關(guān)鍵點(diǎn)
在意鎖的對(duì)象和范圍 -> 減少粒度
注意鎖的對(duì)象本身大小 -> 減少空間占用
注意鎖的速度 -> 提高速度
不同場(chǎng)景選擇不同的并發(fā)類 -> 因需而變
衡量好鎖的價(jià)值 -> 能不用就不用
1窝撵、分析同步問(wèn)題的核心三要素
原子性:“并無(wú)一氣呵成俺夕,豈能無(wú)懈可擊”,如:高并發(fā)下的 i++
可見(jiàn)性:“你做的改變椒惨,別人看不見(jiàn)”
有序性:“不按套路出牌”
2缤至、鎖的分類
競(jìng)爭(zhēng)的態(tài)度:樂(lè)觀鎖(java.util.concurrent 包中的原子類) 與 悲觀鎖(synchronized)
等待鎖的人是否公平:公平鎖 new ReentrantLock(true) 與 非公平鎖 new ReentrantLock()
是否可共享:共享鎖 與 獨(dú)享鎖 - ReadWriteLock,其讀鎖是共享鎖康谆,其寫(xiě)鎖是獨(dú)享鎖
3凄杯、Netty 玩轉(zhuǎn)鎖的五個(gè)關(guān)鍵點(diǎn)
3.1、在意鎖的對(duì)象和范圍 -> 減少粒度
例:初始化 channel (io.netty.bootstrap.ServerBootstrap#init)
Synchronized method -> Synchronized block
void init(Channel channel) throws Exception {
Map<ChannelOption<?>, Object> options = this.options0();
synchronized(options) {
setChannelOptions(channel, options, logger);
}
Map<AttributeKey<?>, Object> attrs = this.attrs0();
synchronized(attrs) {
Iterator var5 = attrs.entrySet().iterator();
while(true) {
if (!var5.hasNext()) {
break;
}
Entry<AttributeKey<?>, Object> e = (Entry)var5.next();
AttributeKey<Object> key = (AttributeKey)e.getKey();
channel.attr(key).set(e.getValue());
}
}
//...
}
3.2秉宿、注意鎖的對(duì)象本身大小 -> 減少空間占用
例:統(tǒng)計(jì)待發(fā)送的字節(jié)數(shù) (io.netty.channel.ChannelOutboundBuffer)
AtomicLong -> Volatile long + AtomicLongFieldUpdater
AtomicLong vs long
前者是一個(gè)對(duì)象戒突,包含對(duì)象頭(object header)以用來(lái)保存 hashcode、lock等信息描睦,32 位系統(tǒng)占用 8 字節(jié)膊存,64 位系統(tǒng)占用 16 字節(jié),所以在 64 位系統(tǒng)下:
- volatile long = 8 bytes
- AtomicLong = 8 bytes (volatile long) + 16 bytes (object header) + 8 bytes (引用) = 32 bytes
至少節(jié)約 24 字節(jié)!
結(jié)論:
Atomic* objects -> Volatile primary type + static Atomic*FieldUpdater
原子類型的對(duì)象可以用 volatile 修飾的基礎(chǔ)類型來(lái)代替隔崎,以節(jié)省空間
3.3今艺、注意鎖的速度 -> 提高速度(提高并發(fā)性)
例1:記錄內(nèi)存分配字節(jié)數(shù)等功能用到的 LongCounter (io.netty.util.internal.PlatformDependent#newLongCounter())
高并發(fā)時(shí):java.util.concurrent.atomic.AtomicLong -> java.util.concurrent.atomic.LongAdder (JDK)
結(jié)論:及時(shí)衡量,使用jdk最新功能
例2:曾經(jīng)根據(jù)不同情況爵卒,選擇不同的并發(fā)包實(shí)現(xiàn):JDK < 1.8 考慮 ConcurrentHashMapV8(ConcurrentHashMap 在 JDK8 中的版本)
3.4虚缎、不同場(chǎng)景選擇不同的并發(fā)類 -> 因需而變
例1:關(guān)閉和等待關(guān)閉事件執(zhí)行器(Event Executor):
Object.wait/notify -> CountDownLatch
io.netty.util.concurrent.SingleThreadEventExecutor#threadLock
例2:Nio Event Loop 中負(fù)責(zé)存儲(chǔ) task 的 Queue
Jdk's LinkedBlockingQueue(MPMC) -> jctools' MPSC
MPMC: muti-producer & muti-consumer
MPSC: muti-producer & simple-consumer
io.netty.util.internal.PlatformDependent.Mpsc#newMpscQueue(int)
3.5、衡量好鎖的價(jià)值 -> 能不用就不用
生活場(chǎng)景:
飯店提供了很多包廂钓株,服務(wù)模式:
一個(gè)服務(wù)員固定服務(wù)某幾個(gè)包廂模式实牡;
所有服務(wù)員服務(wù)所有的包廂模式;
表面上看轴合,前者效率沒(méi)有后者高创坞,但實(shí)際上它避免了服務(wù)員間的溝通(上下文切換)等開(kāi)銷,避免客人與服務(wù)員之間導(dǎo)出亂串受葛,管理簡(jiǎn)單题涨。
局部串行:Channel 的 I/O 請(qǐng)求處理 Pipeline 是串行的
整體并行:多個(gè)串行化線程(Nio Event Loop)
Netty 應(yīng)用場(chǎng)景下:局部串行 + 整體并行 > 一個(gè)隊(duì)列 + 多個(gè)線程模式:
降低用戶開(kāi)發(fā)難度、邏輯簡(jiǎn)單总滩、提升處理性能
避免鎖帶來(lái)的上下文切換和并發(fā)保護(hù)等額外開(kāi)銷
本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布纲堵!