基础设施
HotSpot VM并发的基础设施主要是原子操作、ParkEvent和Parker,后面两个功能的重合度很高,未来可能合并为一个ParkEvent。在笔者写这本书的时候(jdk-12+31),它们两个还是独立的个体,所以文中会分别讨论。
原子操作
原子操作即普通意义上的不可打断的操作。HotSpot VM的原子模块位于runtime/atomic,它实现了原子性的递增值、交换值、比较并交换等操作,其底层实现依赖于CPU指令。举个例子,x86提供lock指令前缀,以保证一个CPU在执行被修饰的指令期间互斥地拥有对应的Cache Line的所有权。这个保证是并发的基础,并发离不开线程,线程离不开锁,如果多个线程在同一时刻抢锁(互斥量/同步量),锁内部就必须有一条只能互斥执行的代码,这便是原子指令。
ParkEvent
使用ParkEvent可以使线程睡眠与唤醒。一个ParkEvent与一个线程的生命周期绑定,当线程结束时,ParkEvent会移到一个EventFreeList链表,而新创建的线程会在EventFreeList中查找ParkEvent,如果没有就分配新的ParkEvent。ParkEvent本身只有分配和释放接口,但是它继承了平台相关的PlaformEvent,因此它就有了PlatformEvent提供的park、unpark接口,如代码清单6-9所示:
代码清单6-9 POSIX PlatformEvent的实现
void os::PlatformEvent::park() {
// CAS递减_event的值
int v;
for (;;) {
v = _event;
if (Atomic::cmpxchg(v - 1, &_event, v) == v) break;
}
if (v == 0) {
int status = pthread_mutex_lock(_mutex);
// 阻塞线程加一
++_nParked;
// 如果递减之后event为-1则阻塞,否则立刻返回
while (_event < 0) {
status = pthread_cond_wait(_cond, _mutex);
}
// 阻塞线程减一
--_nParked;
_event = 0;
status = pthread_mutex_unlock(_mutex);
OrderAccess::fence();
}
}void os::PlatformEvent::unpark() {
// 如果_event大于等于0,则设置为1并返回
if (Atomic::xchg(1, &_event) >= 0) return;
// 仅当_event为-1时,即另一个线程阻塞住时才执行后面的唤醒另一个线程的操作
int status = pthread_mutex_lock(_mutex);
int anyWaiters = _nParked;
status = pthread_mutex_unlock(_mutex);
if (anyWaiters != 0) {
status = pthread_cond_signal(_cond);
}
}
ParkEvent依赖的假设是它只被当前绑定的线程park,但是允许多个线程unpark。了解Windows的读者都知道,Windows内核有可以作为同步工具的Event内核对象。当一个操作完成时,可以将Event对象设置为触发状态,此时等待Event事件的线程将得到通知。ParkEvent在Windows上是通过Event内核对象实现的,由于内核的原生支持,其实现也比POSIX简单不少,如代码清单6-10所示:
代码清单6-10 Windows PlatformEvent的实现
void os::PlatformEvent::park() {
int v;
for (;;) {v = _Event;
if (Atomic::cmpxchg(v-1, &_Event, v) == v) break;
}
if (v != 0) return;
while (_Event < 0) {
DWORD rv = ::WaitForSingleObject(_ParkHandle, INFINITE);
}
_Event = 0;
OrderAccess::fence();
}
void os::PlatformEvent::unpark() {
if (Atomic::xchg(1, &_Event) >= 0) return;
::SetEvent(_ParkHandle);
}
ParkEvent广泛应用于HotSpot VM内部