多线程的着重点以及优秀框架的思想借鉴

本文探讨了多线程编程中的隐患和挑战,如线程安全、活跃性问题和性能开销,并介绍了Disruptor如何通过内存分配和序列化解决这些问题。Disruptor通过预分配内存、序列化管理和批量效应提高性能。此外,文章还提及Netty的线程模型和零拷贝内存优化,展示了其在高性能并发处理中的应用。


多核时代,多线程在高并发上的重要性不必多说

多线程带来的隐患和技术挑战

线程安全

在线程安全类中封装必要的同步机制,保证线程安全性
着重点在访问共享的可变状态时需要进行正确的管理:

  • 只读共享,任何线程都可以访问但不能修改对象
  • 保护对象,通过持有锁进行访问
  • 线程安全共享,对象的内部实现同步
  • 线程封闭,使得对象只能由一个线程拥有

活跃性

当某个操作不能继续执行下去的时候,就会发生活跃性问题,最主要的原因是锁顺序死锁的产生。在设计时一定要排除可能导致死锁出现的条件:

  • 支持定时锁
  • 按照固定的顺序持有锁
    其他活跃性危险:
  • 饥饿
  • 糟糕的响应性
  • 活锁

性能问题

多线程引入的额外操作的开销包括:线程之间的协调、增加上下文切换、线程的创建和销毁、线程调度等
对于多线程的性能问题,Java提供了一些原生的解决方案:不同的锁策略(synchronized关键字、读写显式锁等)、CAS无锁设计以及线程池。

Disruptor

它是一种可替代有界队列完成并发线程间数据交换的高性能解决方案,Disruptor的设计初衷就是为了解决多线程引入的性能问题【锁代价过大、CAS代价过大、缓存行导致的伪共享、队列存在的问题】,以求最优化内存分配,使用缓存友好的方式来最佳使用现代硬件资源。

内存分配

Ring buffer的内存是在启动时预先分配的。预先分配的策略就能够避免Java内存回收引起的一些性能问题,因为这些元素对象(enries)在能够在Disruptor实例中的整个生命周期存活和被复用【注:这些enties一直被RingBuffer对象持有,而RingBuffer实例对象又被Disruptor持有,故只要Disruptor存在,则这些enties便不会被GC掉】。由于这些对象是在开始阶段同时分配的,很大程度上被连续分布在主存中,即使不连续它们在内存中得间隔也有可能是固定的,从而支持缓存步幅,非常有利于缓存的数据预取【硬件操作缓存并不是以字节或字为单位,为了效率考虑,缓存通常以缓存行(cache line)的形式进行组织,缓存行通常有32-256字节,最常见的是64字节。缓存行也是缓存一致性协议操作的最小粒度】。

隔离关注(Teasing Apart the Concerns)

如下几个分开的关注点是所有队列实现方式都需要综合实现的,这些也是为队列实现定义的接口:
  1) 队列元素的存储;
  2) 队列协调生产者声明下一个需要交换的队列元素的序号;
  3) 队列协调并告知消费者等待的元素已经就绪。

序列化(Sequencing)

顺序化是disruptor管理并发的核心概念。每个生产者和消费者都维护自己的序号,这个序号用来和RingBuffer交互。当一个生产者希望在RingBuffer中添加一个元素时,它首先要做的是声明一个序号【注:这个序号被称之为生产者的声明序号,该序号用来指示下一个空闲的slot的位置,一旦序号被声明,那么该序号就不会被其它生产者重复操作,生产者就可以操作该声明序号的slot数据】。单生产者的场景下,这个声明序号可以是一个简单的整数;多生产者的场景,这个序号必须是一个支持CAS的原子变量。当一个生产者序号被声明,这个序号对应的位置就可以被声明该序号的生产者写入了;当生产者完成更新元素后,它就通过更新一个单独的序号来提交变化,这个单独的序号是一个游标(cursor),用来指示消费者可以消费的最新的元素。

批量效应(Batching Effect)

当消费者等待RingBuffer中可用的前进游标序号时,如果消费者发现RingBuffer游标自上次检查以来已经前进了多个序号,消费者可以直接处理所有可用的多个序号,而不用引入并发机制, 这样滞后的消费者能够迅速跟上生产者的步伐,从而平衡系统

Netty

线程模型

Reactor反应器模式,其实,有很多高性能的中间件都是基于反应器模式进行设计的,包括Nginx、Redis、Netty等。
反应器模式由Reactor反应器线程、Handlers处理器两大角色组成:

  • Reactor反应器线程的职责:负责响应IO事件,并且分发到Handlers处理器
  • Handlers处理器的职责:非阻塞的执行业务处理逻辑
    ChannelPipeline,将绑定到一个通道的多个Handler处理器实例,串在一起,形成一条流水线,实现了对多个处理器的编排(所有通道的事件是串行处理的)。

“零拷贝”的内存优化

Zero Copy的模式中,避免了数据在用户空间和内存空间之间的拷贝,从而提高了系统的整体性能。Netty中大量使用基于“零拷贝”的api实现功能,对内存上的数据进行直接操作。

高效的内存管理

Netty使用ByteBuf对象作为数据容器,进行I/O读写操作,Netty的内存管理也是围绕着ByteBuf对象高效地分配和释放。内存管理上,实现了对内存的池化管理,通过归一化处理,使池化内存分配大小规格化,大大方便内存申请和内存、内存复用,提高效率,然后,通过PoolArena对象,实现对多个PoolChunkList、PoolSubpage的分组管理调度,线程安全控制、提供内存分配、释放的服务。在线程处理上,首先,为了减少线程间的竞争,Netty会提前创建多个PoolArena,当线程首次请求池化内存分配,会找被最少线程持有的PoolArena,并保存线程局部变量PoolThreadCache中,实现线程与PoolArena的关联绑定;Netty还设计了缓存机制提升并发性能:当请求对象内存释放,PoolArena并没有马上释放,而是先尝试将该内存关联的PoolChunk和chunk中的偏移位置(handler变量)等信息存入PoolThreadLocalCache中的固定大小缓存队列中(如果缓存队列满了则马上释放内存),当请求内存分配,PoolArena会优先访问PoolThreadLocalCache的缓存队列中是否有缓存内存可用,如果有,则直接分配,提高分配效率。

【完美复现】面向配电网韧性提升的移动储能预布局与动态调度策略【IEEE33节点】(Matlab代码实现)内容概要:本文介绍了基于IEEE33节点的配电网韧性提升方法,重点研究了移动储能系统的预布局与动态调度策略。通过Matlab代码实现,提出了一种结合预配置和动态调度的两阶段优化模型,旨在应对电网故障或极端事件时快速恢复供电能力。文中采用了多种智能优化算法(如PSO、MPSO、TACPSO、SOA、GA等)进行对比分析,验证所提策略的有效性和优越性。研究不仅关注移动储能单元的初始部署位置,还深入探讨其在故障发生后的动态路径规划与电力支援过程,从而全面提升配电网的韧性水平。; 适合人群:具备电力系统基础知识和Matlab编程能力的研究生、科研人员及从事智能电网、能源系统优化等相关领域的工程技术人员。; 使用场景及目标:①用于科研复现,特别是IEEE顶刊或SCI一区论文中关于配电网韧性、应急电源调度的研究;②支撑电力系统在灾害或故障条件下的恢复力优化设计,提升实际电网应对突发事件的能力;③为移动储能系统在智能配电网中的应用提供理论依据和技术支持。; 阅读建议:建议读者结合提供的Matlab代码逐模块分析,重点关注目标函数建模、约束条件设置以及智能算法的实现细节。同时推荐参考文中提及的MPS预配置与动态调度上下两部分,系统掌握完整的技术路线,并可通过替换不同算法或测试系统进一步拓展研究。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值