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 (num.compareAndSet(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的一些概念。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值