提示:想快速解决问题,建议直接点标题中的AtomicInteger对原子操作的常用方法。
文章目录
- 一、AtomicInteger定义
- 二、AtomicInteger使用场景
- 三、AtomicInteger作用
- 四、AtomicInteger对原子操作的常用方法
- 五、AtomicInteger底层原理(源码解析)
- 总结
一、AtomicInteger定义
1.AtomicInteger类是系统底层保护的int类型,通过对int类型的数据进行封装,提供执行方法的控制进行值的原子操作,但AtomicInteger ≠ Integer。
2.AtomicInteger是一个提供原子操作的Integer类,通过线程安全的方式操作加减。
从JAVA 1.5开始,AtomicInteger 属于java.util.concurrent.atomic 包下的一个类。
二、AtomicInteger使用场景
1.AtomicInteger提供原子操作来进行Integer的使用,适合高并发情况下的使用。
2.foreach作循环的时候需要对应参数进行自增或者自减操作。
三、AtomicInteger作用
分析普通Java的运算操作:
Java中的运算操作在多线程是线程不安全的。比如i++解析为i=i+1,Java程序会把算式分为3个操作,获取值,计算值,赋予值,i++这个操作不具备原子性,多线程并发共享变量时必然会出现问题。
原子性:指的就是一个操作是不可中断,即使有多个线程执行,一个操作开始也不会受其他线程影响,即可以理解为线程的最小执行单元,不可被分割。
一个线程计算出值后,还未重新给变量赋值,另一个线程来读取到这个值,就会造成线程不安全的问题,有时候需要通过加锁的方式去保证线程安全,但是加锁对性能会有很大的影响。
AtomicInteger的作用:就是让程序在不加锁的时候也能保障线程安全,场景一案例:
package com.example.test;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import java.util.concurrent.atomic.AtomicInteger;@SpringBootApplicationpublic class TestApplication {static int b =0;public static void main(String[] args) throws InterruptedException{SpringApplication.run(TestApplication.class, args);AtomicInteger a = new AtomicInteger(0);Thread t1 = new Thread(() -> {for (int i = 0; i < 10000; i++) {a.incrementAndGet();b++;}});t1.start();Thread t2 = new Thread(() -> {for (int i = 0; i < 10000; i++) {a.incrementAndGet();b++;}});t2.start();Thread.sleep(1000);System.out.println("a="+a);System.out.println("b="+b);}}
当线程不安全的时候会出现以下结果
因此,使用AtomicInteger在多线程进行自增运算的时候是线程安全的,但普通的int在自增的时候可能线程是不安全的。
场景二在博客: forEach循环报错
四、AtomicInteger对原子操作的常用方法
- addAndGet()- 以原子方式将给定值添加到当前值,并在添加后返回新值。
- getAndAdd() – 以原子方式将给定值添加到当前值并返回旧值。
- incrementAndGet()- 以原子方式将当前值递增1并在递增后返回新值。它相当于i ++操作。
- getAndIncrement() – 以原子方式递增当前值并返回旧值。它相当于++ i 操作。
- decrementAndGet()- 原子地将当前值减1并在减量后返回新值。它等同于i– 操作。
- getAndDecrement() – 以原子方式递减当前值并返回旧值。它相当于– i 操作。
五、AtomicInteger底层原理(源码解析)
基于CAS的乐观锁实现
乐观锁是一种乐观思想,即认为每次取数据得时候都认为别的线程不会正在修改,所以不加锁,写数据的时候判断当前值与期望值是否相等,一样则更新,否则继续进行CAS操作。
unsafe是java提供的获得对对象内存地址访问的类,作用是在更新操作时提供“比较并替换”的作用,实际上就是AtomicInteger中的一个工具。
valueOffset是用来记录value本身在内存的编译地址的,也是为了更新操作在内存中找到value的位置。
unsafe提供了objectFieldOffset()方法用于获取某个字段相对Java对象的“起始地址”的偏移量,也提供了getInt、getLong、getObject之类的方法可以使用前面获取的偏移量来访问某个Java对象的某个字段。
注意:value是用来存储整数的时间变量,这里被声明为volatile,就是为了保证在更新操作时,当前线程可以拿到value最新的值(并发环境下,value可能已经被其他线程更新了)。
在jdk1.8版本之后,AtomicInteger具体是如何实现的原子操作。
首先看incrementAndGet() 方法,Unsafe在jdk1.8已经封装了getAndAddInt可以直接给AtomicXXXXX类使用,下面是具体的代码。
在jdk1.8版本之前,AtomicInteger具体是如何实现的原子操作。
同样看看incrementAndGet() 方法
public final int incrementAndGet() {for (;;) {int current = get();int next = current + 1;if (compareAndSet(current, next))return next;}}
先获取到当前的 value 属性值,然后将 value 加 1,赋值给一个局部的 next 变量,然而,这两步都是非线程安全的,但是内部有一个死循环,不断去做compareAndSet操作,直到成功为止,也就是修改的根本compareAndSet方法里面。
compareAndSet()方法的代码如下:
public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}
其中调用的compareAndSwapInt方法代码如下:
publicfinal native boolean compareAndSwapInt(Object var1, long var2, int var4, intvar5);
compareAndSet 传入的为执行方法时获取到的 value 属性值,next 为加 1 后的值, compareAndSet所做的为调用 Sun 的 UnSafe 的 compareAndSwapInt 方法来完成,此方法为 native 方法,compareAndSwapInt 基于的是CPU 的 CAS指令来实现的。所以基于 CAS 的操作可认为是无阻塞的,一个线程的失败或挂起不会引起其它线程也失败或挂起。并且由于 CAS 操作是 CPU 原语,所以性能比较好。
总得来说,就是最终通过compareAndSwapInt方法,循环操作对传进来的旧值和新值进行对比和更新,两值不一致就进行更新操作,从而达到自增的结果。
总结
以上就是今天要分享的内容,如果有什么不懂的地方或者有什么出错的地方,欢迎前来探讨,谢谢大家~
能够帮助你们解决问题是博主的荣幸,你们的支持是我创作的最大动力!:)欢迎关注点赞