Netty的那些“锁”事儿

·分析同步问题的核心三要素

原子性:“并无一气呵成,岂能无懈可击”

可见性:“你做的改变,别人看不见”

有序性:“不按套路出牌”

·锁的分类

对竞争的态度:乐观锁(java.util.concurrent包中的原子类)与悲观锁(Synchronized)

等待锁的人是否公平而言:公平锁new ReentrantLock (true)与非公平锁new ReentrantLock ()

是否可以共享:共享锁与独享锁:ReadWriteLock,其读锁是共享锁,其写锁是独享锁

.Netty玩转锁的五个关键点:

·在意锁的对象和范围->减少粒度

image

新版本的这块儿代码已经被重构了,换了别的实现方式。

但这个例子本身就你能说明问题

·注意锁的对象本身大小->减少空间占用

image

Atomic long vs long:
前者是一个对象,包含对象头(object header)以用来保存 hashcode、lock等信息,32位系统占用8字节;64位系统占16字节,所以在64位系统情况下:

  • volatile long = 8 bytes
  • AtomicLong = 8 bytes (volatile long) + 16bytes(对象头)+8 bytes(引用)= 32 bytes

至少节约24字节!

结论:Atomic* objects -> Volatile primary type + Static Atomic*FieldUpdater

·注意锁的速度->提高并发性

例1∶记录内存分配字节数等功能用到的LongCounter

( io.netty.util.internal.PlatformDependent#newLongCounter() )

高并发时: java.util.concurrent.atomic.AtomicLong ->java.util.concurrent.atomic.LongAdder (JDK11)

结论:及时衡量、使用JDK最新的功能

image

image

不同场景选择不同的并发类-→>因需而变

例1:关闭和等待关闭事件执行器(Event Executor) :

object.wait/notify -> CountDownLatch

io.netty.util.concurrent.SingleThreadEventExecutor#threadLock:

image

例2: Nio Event loop中负责存储task的Queue

Jdk's LinkedBlockingQueue (MPMC) -> jctools' MPSC

io.netty.util.internal.PlatformDependent.Mpsc#newMpscQueue(int):

image

针对多生产者单消费者——自己实现了一个锁

MPMC——multi producermulti multi consumer

MPSC—— multi producermulti sigle consumer

衡量好锁的价值->能不用则不用

局部串行:Channel的I/O请求处理Pipeline是串行的

image

整体并行:多个串行化的线程(NioEventLoop)

image

Netty应用场景下:

局部串行+整体并行>一个队列+多个线程模式:

·降低用户开发难度、逻辑简单、提升处理性能

·避免锁带来的上下文切换和并发保护等额外开销

避免用锁:用ThreadLocal来避免资源争用,例如Netty轻量级的线程池实现

io.netty.util.Recycler#threadLocal

image

posted @ 2022-04-22 17:20  飞飞很要强  阅读(132)  评论(0)    收藏  举报