100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > java中的变量是原子的_Java原子变量

java中的变量是原子的_Java原子变量

时间:2023-11-29 16:21:04

相关推荐

java中的变量是原子的_Java原子变量

概述

多个线程操作共享变量(Java堆内存上的数据)会带来bug,Java提供了锁机制(Lock)来管理多线程并发,比如synchronized,但是会带来额外的性能开销(线程阻塞,上下文切换等)。为了提升性能,Java引入了原子变量,通过无锁算法(lock-free)实现多线程安全,比如CAS。

原子变量只是实现多线程安全的一个手段,在对单个共享变量进行”读取-修改-写入“操作的场景下很适合,所以,其适用场景没有synchronized广泛。

多线程问题

首先,实现一个计数器,代码如下:

public class Counter {

private volatile int num;

public void increment() {

num++;

}

public static void main(String[] args) {

Counter counter = new Counter();

// 多线程递增计数器

IntStream.range(0, 100).parallel().forEach(i -> counter.increment());

// 打印结果

System.out.println("counter: " + counter.num);

}

}

多次运行上述代码,打印出来的值不是100,而是98,97等。

这是一个典型的多线程问题,num++ 看似一行简单的代码,像是一个原子操作,其实则不然,递增操作可能会三个步骤进行:

读取当前num变量的值

执行num+1

将+1后的值赋值给num变量

所以,多个线程更新后的值会出现覆盖的情况,比如两个线程同时拿到了num的值为50,在各自的线程中执行加法操作后为51,然后更新主存中的值为51,但是我们期望的值是52。

通过synchronized解决

给increment()方法增加synchronized关键字,如下:

// ...

public synchronized void increment() {

num++;

}

// ...

synchronized是Java中最常用的锁,保证被“监控”代码块在同一个时刻只能由一个线程执行,所以最终出来的结果为100,正确。

但是,该方法会导致没获取锁的线程挂起,发生上下文切换,这就是重量级锁带来的性能开销。

通过原子变量AtomicInteger解决

atomic包下有AtomicInteger类,可以解决上述问题,代码如下:

public class Counter {

private AtomicInteger num = new AtomicInteger(0);

public void increment() {

while (true) {

int oldValue = num.get();

int newValue = oldValue + 1;

if (pareAndSet(oldValue, newValue)) {

return;

}

}

}

public static void main(String[] args) {

Counter counter = new Counter();

// 多线程递增计数器

IntStream.range(0, 100).parallel().forEach(i -> counter.increment());

// 打印结果

System.out.println("counter: " + counter.num);

}

}

运行代码,输出结果100。

CAS原子操作

Java并发包下的原子变量利用了CAS机制,实现了原子操作。这儿说的原子操作,是指CPU对某一块内存的原子操作(Atomic memory operation),具备如下特点:

串行化多个线程对同一块内存的更新操作(保证多线程更新数据时的安全)。

读取-修改-写入这三个操作不可被中断,更新操作要不然成功,要不然失败,不会出现中间状态(保证数据完整性)。

只有当内存中的值与期望值相同时,才会执行更新操作(保证正确的逻辑)。

在并发编程中,CAS属于”乐观锁“,假设多线程竞争几率很小,或者在很短的时间内竞争状态会结束,如果多线程竞争非常频繁,会使CPU长时间空转(busy waiting),造成资源浪费。所以,没有银弹!根据场景选择技术方案。

CAS(Compare And Swap)需要特定的CPU指令支持,所以并不是所有硬件平台都支持CAS。Java跨平台的特性要求API的行为一致性,所以在不支持CAS的硬件平台上,atomic会退化成重量级锁。

总结

实现多线程的手段很多,根据场景选择合理的技术方案可以提升程序的性能。本文简单讲述了Java中原子变量是如何解决多线程问题,以及CAS的一些概念。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。