博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java Atomic类使用
阅读量:2426 次
发布时间:2019-05-10

本文共 5609 字,大约阅读时间需要 18 分钟。

JUC包中除了锁,还提供了原子操作类来实现线程对临界资源的互斥访问。

Atomic包中提供了多种类型的原子操作类:

在这里插入图片描述
它们都是CAS(compareAndSwap)来实现原子性。

1.原子基本类型

用于原子更新基本类型,包括以下三类:

  • AtomicBoolean:原子更新布尔类型
  • AtomicInteger:原子更新整数类型
  • AtomicLong:原子更新长整数类型

AtomicInteger 常用方法:

  • int incrementAndGet():将当前值加1并返回新值。
  • int decrementAndGet():将当前值减1并返回新值
  • int updateAndGet(IntUnaryOperator updateFunction):将当前值原子执行用户自定义的操作后并返回新值。(updateFunction是用户自定义操作)
public class AtomicIntegerTest {
public static void main(String[] args) {
AtomicInteger integer = new AtomicInteger(0); //递增 System.out.println("递增后的值: " + integer.incrementAndGet()); //递减 System.out.println("递减后的值: " + integer.decrementAndGet()); //增加20 System.out.println("增加20后的值: " + integer.updateAndGet((x)-> x + 20)); }}

运行结果:

递增后的值: 1递减后的值: 0增加20后的值: 20

2.原子引用类型

原子基本类型只能保证基本类型的原子性,如果要原子更新一个对象就需要使用原子引用类型。

原子引用类型如下:

  • AtomicReference:原子更新引用类型
  • AtomicMarkableReference:AtomicMarkableReference跟AtomicStampedReference差不多,AtomicStampedReference关心的是动过几次,AtomicMarkableReference关心的是有没有被人动过。
  • AtomicReferenceFieldUpdater:原子更新用类型中的字段。
  • AtomicStampedReference:原子更新引用类型,只是该引用是带版本号的,用于解决ABA问题。

AtomicReference 主要方法:

  • V get():获取当前引用的值。
  • boolean compareAndSet(V expect, V update):如果当前引用等于预期引用,则将当前引用更新为新值。

下面以多个线程更新账户余额的例子来说明AtomicReference的使用

/*账户余额*/class AccountBalance {
private BigDecimal banlance; public AccountBalance(BigDecimal banlance) {
this.banlance = banlance; } public BigDecimal getBanlance() {
return banlance; } public void setBanlance(BigDecimal banlance) {
this.banlance = banlance; }}class UpdateBalanceRunnable implements Runnable {
private AccountBalance accountBalance; private AtomicReference
reference; public UpdateBalanceRunnable(AtomicReference
reference, AccountBalance accountBalance) {
this.reference = reference; this.accountBalance = accountBalance; this.reference.set(this.accountBalance); } @Override public void run() {
while(true) {
AccountBalance prev = reference.get(); // 账户余额加10 AccountBalance next = new AccountBalance(prev.getBanlance(). add(new BigDecimal(10.0))); if (reference.compareAndSet(prev, next)){
//更新成功,退出,否则自旋 break; } } }}public class AtomicReferenceTest {
public static void main(String[] args) throws InterruptedException {
AccountBalance first = new AccountBalance(new BigDecimal(0)); System.out.println("更新前账户余额: " + first.getBanlance()); AtomicReference
reference = new AtomicReference<>(first); UpdateBalanceRunnable runnable = new UpdateBalanceRunnable(reference,first); Thread t1 = new Thread(runnable); Thread t2 = new Thread(runnable); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("更新后账户余额: " + reference.get().getBanlance()); }}

运行结果:

更新前账户余额: 0更新后账户余额: 20

AtomicStampedReference主要方法:

  • V get(int[] stampHolder):获取指定版本的值
  • public V getReference():返回当前版本的值,也就是最新值。
  • public int getStamp():返回当前版本戳。
  • public boolean compareAndSet(V expectedReference, V newReference,
    int expectedStamp, int newStamp):如果当前引用 等于 预期值并且 当前版本戳等于预期版本戳, 将更新新的引用和新的版本戳到内存
  • public boolean attemptStamp(V expectedReference, int newStamp):如果当前引用 等于 预期引用, 将更新新的版本戳到内存
  • public void set(V newReference, int newStamp) :设置当前引用的新引用和版本戳
public class AtomicStampedReferenceTest {
public static void main(String[] args) {
Integer integer1 = 0; Integer integer2 = 2; AtomicStampedReference
reference = new AtomicStampedReference
(integer1,1); reference.compareAndSet(integer1,integer2,reference.getStamp(),reference.getStamp()+1); System.out.println("更新value后:" + reference.getReference()); boolean b = reference.attemptStamp(integer2, reference.getStamp() + 1); System.out.println("更新版本后: "+reference.getStamp()); boolean c = reference.compareAndSet(integer2,3,4, reference.getStamp()+1); if (!c) {
System.out.println("CAS 操作失败"); } }}

运行结果:

更新value后:2更新版本后: 3CAS 操作失败

3.原子更新数组类型

Atomic包同样提供了对数组类型原子操作的类,主要包括:

  • AtomicIntegerArray:整型数组原子操作类
  • AtomicLongArray:长整型数组原子操作类
  • AtomicReferenceArray:对象数组原子操作类

AtomicIntegerArray主要方法:

  • int incrementAndGet(int i):将数组位置i的元素加1
  • boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式将数组位置i的元素设置成update值

下面是一个多线程更新数组的例子。

public class AtomicIntegerArrayTest {
public static void main(String[] args) throws InterruptedException {
AtomicIntegerArray array = new AtomicIntegerArray(10); List
list = new ArrayList
(); System.out.println("数组未修改前:"); for (int i = 0; i < array.length(); ++i) {
System.out.print(array.get(i) + "\t"); } for (int i = 0; i < 10; ++i) {
Thread t = new Thread(() -> {
//依次更新数组 for (int j = 0 ; j < array.length(); ++j) {
array.incrementAndGet(j); } }); list.add(t); t.start(); } for (Thread t: list) {
t.join(); } System.out.println("\n数组被修改后:"); for (int i = 0; i < array.length(); ++i) {
System.out.print(array.get(i) + "\t"); } }}

运行结果:

数组未修改前:0	0	0	0	0	0	0	0	0	0	数组被修改后:100	100	100	100	100	100	100	100	100	100

转载地址:http://oecmb.baihongyu.com/

你可能感兴趣的文章
张一鸣无圈胜破圈?
查看>>
抓紧!抓紧!CSDN年终重榜福利来了~人手一份,快来投稿!!
查看>>
干货! AI 推断解决方案栈 Vitis AI 全流程独家解析
查看>>
真相了 | 敲代码时,程序员戴耳机究竟在听什么?
查看>>
回首互联网十年,我们能从八次烧钱大战中学到什么
查看>>
漫画:如何辨别二逼互联网公司!?
查看>>
麒麟信安面向场景化创新,赋能openEuler商业验证
查看>>
王者又连跪了?快让 AI 帮你上分!
查看>>
1 分钟带你认识从 "�" 到 "锟斤拷"
查看>>
3 年培养 10 万“码农”,郑州推出“码农计划”
查看>>
一个三本程序猿的大厂逆袭之路
查看>>
程序员弃码投中医?还做成了不错的生意! | 极客视频
查看>>
百度一 29 岁程序员因“篡改数据”被抓
查看>>
去年我年薪 30W,今年我一天做 3 顿饭
查看>>
入职大厂,我容易吗?
查看>>
《互联网人退化简史》
查看>>
CTO 写的低级 Bug 再致网站被黑,CEO 的号都被盗了!
查看>>
955 加班少的公司名单来了!
查看>>
狂赚 1227 亿!腾讯员工 2020 年人均年薪 81 万;小米员工人均年薪 45 万
查看>>
漫画:什么是加密算法?
查看>>