发布于2026-07-09 阅读(0)
扫一扫,手机访问
在实现自旋与阻塞结合的锁机制时,synchronized 并不是一个好选择——它的内部策略是完全黑盒的,开发者无法控制何时自旋、何时阻塞。而 LockSupport 提供了一把精确的手术刀:park() 和 unpark() 可以让你精准地控制每一个线程的挂起与唤醒。那么,如何用它构建一个比标准 synchronized 更灵活的锁?以下是一些关键要点和实践陷阱。
synchronized 做不到自旋+阻塞的组合?synchronized 是 JVM 层硬编码的锁机制,它没有提供任何接口让你干预“尝试获取失败后是否自旋”或“何时转为阻塞”。它的策略完全是黑盒的:可能自旋几轮就挂起,也可能直接进队列。开发者只能被动接受,无法根据业务场景进行优化。
而 LockSupport 则完全不同。它提供了精确的线程状态控制能力——park() 和 unpark() 的调用时机、目标线程、是否带超时,全由你决定。这才是构建高性能锁的基础。
park() 前必须做哪些检查?直接调用 park() 等于把线程扔进 WAITING 状态,之后只能靠 unpark() 或中断唤醒。在锁场景下,这样做会导致严重问题:
park() 不抛异常,线程被中断后仍处于 WAITING,但 Thread.interrupted() 已清零,容易漏处理。正确的做法是:先用 CAS 尝试获取锁;失败后进入有限次自旋(比如 100–500 次),期间不断读取锁状态;自旋耗尽仍未成功,再调用 LockSupport.park(this) 阻塞自己。
unpark() 精确唤醒等待者?标准的 Object.notifyAll() 或 Condition.signalAll() 会唤醒所有等待线程,造成惊群效应。而 LockSupport.unpark(Thread) 只唤醒指定线程,这是构建公平/非公平组合锁的关键。
实操中有几点需要注意:
AtomicReferenceFieldUpdater + 自定义 Node 链表),每次 park() 前把自己入队,unpark() 时只唤醒队首。Thread.currentThread() 以外的线程引用——如果等待线程已退出或被 GC,unpark() 无效但不报错,需配合状态标记(如 volatile int state)判断是否仍需唤醒。unpark();顺序颠倒可能导致唤醒后立即又抢锁失败,重新 park。这两个参数没有银弹值,但存在明确的踩坑边界:
parkNanos(long) 的纳秒值别硬写常量——JVM 对极小超时(如 <1ms)的处理在不同版本和平台上表现各异,建议实测验证。parkUntil(long) 而不校验状态:系统时钟可能被 NTP 调整,deadline 突然跳变会导致线程提前或永久阻塞。最容易被忽略的一点:park() 不保证内存可见性自动刷新,必须在 park 前插入 volatile 读(如读一个 volatile boolean lockFree),否则可能因 CPU 缓存未更新而错过唤醒信号。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8