【问题标题】:Thread safety & atomicity of java semaphorejava信号量的线程安全和原子性
【发布时间】:2018-08-17 11:29:24
【问题描述】:

它们是如何实现的?我看过那个类的代码,看起来并没有使用任何同步机制来确保线程安全或函数调用的原子性。

我指的是班级java.util.concurrent.Semaphore

编辑:请理解这绝不是错误报告或对 Java 技术的不信任。相反,请求让我理解。

【问题讨论】:

  • 注意到class Sync extends AbstractQueuedSynchronizer?
  • Semaphore 使用AbstractQueuedSynchronizer(正如@assylias 所说,我只是想链接到文档)。

标签: java thread-safety semaphore atomic


【解决方案1】:

简介

在更广泛的镜头中,问题变成了任何锁定机制如何实现线程安全。由于这个问题有几个部分,我将逐步进行。

比较和交换 (CAS)

Compare-and-Swap (CAS) 是机器级别的 atomic 指令和程序级别的原子操作。现在,在您的特定信号量问题中,他们在从信号量访问许可时利用了这种技术。

  1. 获取当前值
  2. 计算新值
  3. 通过传入 当前值新值 执行 CAS 操作,查看该内存访问处的值是否为当前值价值并将其与新的交换。如果它确实保存了我们注意到的当前值,则该值存储在内存位置并返回 true。如果它找到与我们预期的当前值不同的值,它不会修改内存位置并返回 false。

AbstractQueuedSynchronizer

AbstractQueuedSynchronizer 类是一个实现类,用于在获取许可时处理同步。该实现创建了一个FIFO queue(尽管可以使用不同类型的队列)并利用park/unpark技术来处理线程运行状态。

停放/取消停放

当信号量中没有可用的许可时,线程将停放。相反,一旦许可可用,线程将 unpark(假设您使用 FIFO 实现,第一个将 unpark)并尝试 CAS操作以获得许可证。如果失败,它将停放并重复该过程。

结论

这些概念协同工作,通过机器级指令和编程操作利用原子性,以确保一次仅由单个线程获取许可/锁,从而将线程对逻辑块的访问限制为所需数量。

【讨论】:

    【解决方案2】:

    了解这一点的最佳方法是查看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 是一个本地函数,它保证原子性,因此在我们的例子中 - 同步。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-02
      • 2021-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多