java 信号量的线程安全性和原子性
Thread safety & atomicity of java semaphore
它们是如何实现的?我看过那个 class 的代码,看起来没有使用任何类型的同步机制来确保线程安全或函数调用的原子性。
我指的是classjava.util.concurrent.Semaphore
编辑:请理解这绝不是错误报告或对 java 技术的不信任。相反,一个让我理解的请求。
最好的理解方式是查看 nonfairTryAcquireShared()
:
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
所以,我们忙循环尝试获取状态,然后我们尝试获取它。
最有趣的部分在compareAndSetState()
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
compareAndSwapInt
是一个本地函数,它保证了原子性,因此在我们的例子中是同步的。
简介
在更广泛的镜头中,问题变成了任何锁定机制如何实现线程安全。由于这个问题有几个部分,我将逐步解决它。
比较和交换 (CAS)
Compare-and-Swap (CAS) is an atomic 机器级指令和程序级原子操作。现在,在您的特定信号量问题中,他们在从信号量访问许可时使用此技术。
- 获取当前值
- 计算新值
- 通过传入当前值和新值执行CAS操作,查看该内存访问处的值是否为当前值并将其与新值交换。如果它确实保存了我们记下的当前值,则该值存储在内存位置并且 return 为真。如果它发现一个与我们期望的当前值不同的值,它不会修改内存位置和 return false.
AbstractQueuedSynchronizer
AbstractQueuedSynchronizer class is an implemented class to handle synchronization while acquiring permits. The implementation creates a FIFO queue(虽然可以使用不同类型的队列)并利用park/unpark技术来处理线程运行状态。
Park/Unpark
当信号量中没有可用许可时,线程将停放。相反,线程将 unpark 一旦许可可用(假设您正在使用 FIFO 实现,第一个将 unpark)并尝试 CAS操作获取许可。如果失败,它将 park 并重复该过程。
结论
这些概念一起工作,通过机器级指令和编程操作利用原子性来确保 permits/locks 一次只能由单个线程获取,从而将线程对逻辑块的访问限制在所需的数量。
它们是如何实现的?我看过那个 class 的代码,看起来没有使用任何类型的同步机制来确保线程安全或函数调用的原子性。
我指的是classjava.util.concurrent.Semaphore
编辑:请理解这绝不是错误报告或对 java 技术的不信任。相反,一个让我理解的请求。
最好的理解方式是查看 nonfairTryAcquireShared()
:
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
所以,我们忙循环尝试获取状态,然后我们尝试获取它。
最有趣的部分在compareAndSetState()
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
compareAndSwapInt
是一个本地函数,它保证了原子性,因此在我们的例子中是同步的。
简介
在更广泛的镜头中,问题变成了任何锁定机制如何实现线程安全。由于这个问题有几个部分,我将逐步解决它。
比较和交换 (CAS)
Compare-and-Swap (CAS) is an atomic 机器级指令和程序级原子操作。现在,在您的特定信号量问题中,他们在从信号量访问许可时使用此技术。
- 获取当前值
- 计算新值
- 通过传入当前值和新值执行CAS操作,查看该内存访问处的值是否为当前值并将其与新值交换。如果它确实保存了我们记下的当前值,则该值存储在内存位置并且 return 为真。如果它发现一个与我们期望的当前值不同的值,它不会修改内存位置和 return false.
AbstractQueuedSynchronizer
AbstractQueuedSynchronizer class is an implemented class to handle synchronization while acquiring permits. The implementation creates a FIFO queue(虽然可以使用不同类型的队列)并利用park/unpark技术来处理线程运行状态。
Park/Unpark
当信号量中没有可用许可时,线程将停放。相反,线程将 unpark 一旦许可可用(假设您正在使用 FIFO 实现,第一个将 unpark)并尝试 CAS操作获取许可。如果失败,它将 park 并重复该过程。
结论
这些概念一起工作,通过机器级指令和编程操作利用原子性来确保 permits/locks 一次只能由单个线程获取,从而将线程对逻辑块的访问限制在所需的数量。