跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

如何通过Java并发编程提升高频交易系统的响应时间_低延迟技术总结

为什么
volatile
不能替代
synchronized
在订单匹配场景中 高频交易里最常误用的,就是以为加个
volatile
就能安全读写共享订单簿。它只保证可见性和禁止重排序,不提供原子性——比如
price = Math.min(price, newPrice)
这种复合操作,哪怕
price
是
volatile
,依然可能丢更新。 真实场景:多个线程同时对
OrderBook.bestAsk
做“取当前值→比较→更新”三步,
volatile
完全拦不住竞态 正确做法:用
AtomicLongFieldUpdater
或
VarHandle.compareAndSet()
替代锁,比
synchronized
更轻量,且能保证 CAS 原子性 坑点:
AtomicInteger
看似方便,但若需更新对象字段(如
Order.price
),必须用
AtomicIntegerFieldUpdater
,否则编译报错
java.lang.IllegalAccessException
如何让
Disruptor
真正跑满 CPU 而不卡在内存屏障上
Disruptor
的吞吐优势依赖无锁 + 内存预分配,但默认配置下常因缓存行伪共享或序列号等待拖慢——尤其在多生产者往同一 RingBuffer 写入时。 关键配置:必须设
WaitStrategy
为
BusySpinWaitStrategy
(非默认的
BlockingWaitStrategy
),否则线程休眠唤醒开销直接吃掉微秒级优势 伪共享防护:用
@Contended
标记
RingBuffer
中的
cursor
、
gatingSequences
字段,JVM 启动加
-XX:-RestrictContended
实测差异:某做市商系统将
YieldingWaitStrategy
换成
BusySpinWaitStrategy
后,P99 延迟从 18μs 降到 5.2μs,但 CPU 使用率升了 37%,得权衡
ForkJoinPool.commonPool()
在行情解码时为何反而拉高 GC 压力 很多团队用
CompletableFuture.supplyAsync()
并行解析 UDP 行情包,背后默认走
ForkJoinPool.commonPool()
。问题在于它的并行度固定为
CPU核心数 - 1
,且无法回收空闲线程——大量短生命周期的
ByteBuffer
解析任务会反复触发 G1 的
G1 Evacuation Pause
。 替代方案:显式创建
ForkJoinPool
,设
parallelism = Runtime.getRuntime().availableProcessors()
,并用
new ForkJoinPool(..., true)
开启 asyncMode 更优选择:对纯 CPU 密集型解码(如 FIX/FAST),直接用
ThreadLocal
缓存
Decoder
实例 + 固定大小
ThreadPoolExecutor
,避免任务排队和线程切换 典型错误:
supplyAsync(() -> decode(buffer), commonPool)
中 buffer 若来自堆外内存(
DirectByteBuffer
),commonPool 线程未及时清理 cleaner,易触发
java.lang.OutOfMemoryError: Direct buffer memory
Netty 的
SO_REUSEADDR
和
SO_LINGER
在断连重试时怎么配才不丢单 交易所网关频繁重建连接时,若内核 TIME_WAIT 连接堆积,新连接可能被拒绝;而
SO_LINGER
设错又会导致 FIN 包未发完就关闭,订单确认消息直接消失。 Eclipse导入Android或其他的JAVA项目的正确方法 WORD版 本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看 下载 立即学习 “ Java免费学习笔记(深入) ”;
SO_REUSEADDR = true
必须开启,否则端口复用失败,
java.net.BindException: Address already in use
高频出现
SO_LINGER
值设为
0
(即强制 RST 关闭)——高频场景下绝不设正数,否则 linger 期间若对方没收到 ACK,订单状态机就卡死 补充动作:在
ChannelInactive
事件里主动调用
orderManager.flushPendingOrders()
,把未确认订单转到本地重试队列,别依赖 TCP 可靠性兜底 实际压测发现,低延迟不是堆砌技术点,而是每个环节都得把“内存访问路径”和“内核交互次数”抠到小数点后一位。比如一个
Unsafe.getAndAddLong
调用,比
AtomicLong.addAndGet()
快 12ns,但若用错 volatile 语义,这 12ns 全白搭。

相关文章