原子操作是如何工作的,线程如何不能被抢占。是OS保证还是JVM保证?

问题描述 投票:3回答:1

我想了解原子操作的工作原理,尤其是在Java中。

AtomicInteger. 文件上说它是: "一个可以原子化更新的int值" "例如,对于这个类来说,其中一个原子操作是。

/**
 * Atomically sets to the given value and returns the old value.
 *
 * @param newValue the new value
 * @return the previous value
 */
public final int getAndSet(int newValue) {
    return unsafe.getAndSetInt(this, valueOffset, newValue);
}

根据文档,它保证这是一个原子操作。然而,实际的方法是 unsafe.getAndSetInt() 会有至少几行执行。那么如何保证原子性呢?

例如,如果Thread-A当前正在执行这段代码,为什么不能抢先执行?我的理解是OS的调度器会把时间片分享给其他线程,如何决定如果一个Thread正在执行某个原子方法,那么所有指令都需要被执行。

这种安排是在OS层面完成的吗?JVM、API Call和OS之间是否有一个契约,如果一个Thread执行someFoo()方法(假设是原子的),那么它就是原子的,需要由该Thread完成,并且不被抢占?

java multithreading atomic
1个回答
2
投票

在一些架构上,有一些指令可以保证原子地读取一个旧值并写入一个新值。 另一些则使用一对称为 "加载链接条件存储"(llcs)的操作。 加载链接操作将加载一个值,并配置硬件来观察是否有任何可能干扰它的事情发生。 只有在上次加载链接后没有发生任何可能会干扰所写值的事情时,条件存储才会写入该值,并且if会指示(例如通过设置寄存器为0或1)存储是否成功。 如果例如conditionalStore成功时返回1,那么交换操作可以实现为。

int exchange(int *ptr, int newValue)
{
  int oldValue;
  do
  {
    oldValue = loadLinked(ptr);
  } while (!conditionalStore(ptr, newValue);
  return oldValue;
}

如果在链接负载和条件存储之间有任何东西干扰了指定的地址,条件存储除了返回0之外,不会有任何副作用。 如果发生这种情况,循环将重复链接加载和条件存储,直到这两个操作设法连续发生。

请注意,许多架构保证存储条件总是在某个位置受到干扰时报告失败,但它们一般不承诺在没有受到干扰时总是报告成功。 根据架构的不同,可能会有很多或很少的误报,像交换这样的操作的效率可能会受到有多少误报的影响。 例如,当另一个线程将任何东西写入与被监视对象相同的缓存行时,一个实现可能会导致条件存储失败,即使其他线程在该缓存行中写入了不同的对象。 这可能会潜在地降低性能,但如果唯一可能导致条件存储失败的事情是在另一个线程上存储成功,那么程序集体仍然可以取得进展。 如果两个线程各自做一个加载链接,然后各自做一个条件存储的效果是两个存储都会失败,那么线程就有可能出现活锁。 这可以通过增加随机延迟来缓解,但在大多数使用场景下,这就没有必要了。

© www.soinside.com 2019 - 2024. All rights reserved.