CodeGym/Java 课程/模块 3/Java 中的原子操作

Java 中的原子操作

可用

原子操作出现的先决条件

让我们看一下这个示例,以帮助您了解原子操作的工作原理:

public class Counter {
    int count;

    public void increment() {
        count++;
    }
}

当我们有一个线程时,一切都很好,但是如果我们添加多线程,我们会得到错误的结果,这都是因为增量操作不是一个操作,而是三个:获取当前值的请求数数,然后将其递增 1 并再次写入数数.

当两个线程想要增加一个变量时,您很可能会丢失数据。也就是说,两个线程都收到 100,因此,两者都将写入 101 而不是预期值 102。

以及如何解决?你需要使用锁。synchronized关键字有助于解决这个问题,使用它可以保证一个线程一次访问该方法。

public class SynchronizedCounterWithLock {
    private volatile int count;

    public synchronized void increment() {
        count++;
    }
}

另外,您需要添加volatile关键字,以确保线程间引用的正确可见性。我们在上面回顾了他的工作。

但仍有缺点。最大的一个是性能,在那个时间点,当许多线程试图获取锁并且一个获得写机会时,其余线程将被阻塞或挂起,直到线程被释放。

所有这些进程、阻塞、切换到另一个状态对于系统性能来说都是非常昂贵的。

原子操作

该算法使用低级机器指令,例如比较和交换(CAS,compare-and-swap,确保数据完整性并且已经有大量研究)。

典型的 CAS 操作对三个操作数进行操作:

  • 工作内存空间(M)
  • 变量的现有预期值 (A)
  • 要设置的新值 (B)

CAS 以原子方式将 M 更新为 B,但前提是 M 的值与 A 相同,否则不采取任何操作。

在第一种和第二种情况下,都会返回M的值,这样可以将获取值、比较值、更新值三个步骤结合起来。这一切都变成了机器级别的一次操作。

当多线程应用程序访问变量并尝试更新它并应用 CAS 时,其中一个线程将获取它并能够更新它。但与锁不同的是,其他线程只会收到无法更新值的错误。然后他们将继续进行进一步的工作,而这种工作完全排除了转换。

在这种情况下,逻辑变得更加困难,因为我们必须处理 CAS 操作未成功运行的情况。我们将只对代码建模,以便在操作成功之前它不会继续。

原子类型介绍

您是否遇到过需要为最简单的int类型变量设置同步的情况?

我们已经介绍过的第一种方法是使用volatile + synchronized。但也有特殊的 Atomic* 类。

如果我们使用 CAS,那么与第一种方法相比,操作工作得更快。此外,我们还有特殊且非常方便的方法来添加值以及递增和递减操作。

AtomicBoolean AtomicInteger AtomicLong AtomicIntegerArray AtomicLongArray是其中操作是原子的类。下面我们就和他们一起分析一下工作。

原子整数

AtomicInteger类除了提供扩展的原子操作外,还提供对可以原子方式读取和写入的int值的操作。

它具有getset方法,其工作方式类似于读取和写入变量。

也就是说,“先于发生”与我们之前讨论过的相同变量的任何后续接收。原子compareAndSet方法也具有这些内存一致性功能。

所有返回新值的操作都是原子执行的:

int addAndGet (int 增量) 将特定值添加到当前值。
布尔比较和设置(预期 int,更新 int) 如果当前值与预期值匹配,则将值设置为给定的更新值。
int decrementAndGet() 将当前值减一。
int getAndAdd(int 增量) 将给定值添加到当前值。
int getAndDecrement() 将当前值减一。
int getAndIncrement() 将当前值增加一。
int getAndSet(int newValue) 设置给定值并返回旧值。
int incrementAndGet() 将当前值增加一。
惰性集(int newValue) 最后设置为给定值。
布尔 weakCompareAndSet(预期,更新 int) 如果当前值与预期值匹配,则将值设置为给定的更新值。

例子:

ExecutorService executor = Executors.newFixedThreadPool(5);
IntStream.range(0, 50).forEach(i -> executor.submit(atomicInteger::incrementAndGet));
executor.shutdown();
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.HOURS);

System.out.println(atomicInteger.get()); // prints 50
1
任务
模块 3,  第 19 级课程 1
已锁定
Earn a Million!
task4201
1
任务
模块 3,  第 19 级课程 1
已锁定
Early Bird Gets the Worm
task4202
评论
  • 受欢迎
你必须先登录才能发表评论
此页面还没有任何评论