Java Integer原子操作——AtomicInteger

  • Post author:
  • Post category:java


提示:想快速解决问题,建议直接点标题中的

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;
@SpringBootApplication
public 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对

原子操作

的常用方法

  1. addAndGet()- 以原子方式将给定值添加到当前值,并在添加后返回新值。
  2. getAndAdd() – 以原子方式将给定值添加到当前值并返回旧值。
  3. incrementAndGet()- 以原子方式将当前值递增1并在递增后返回新值。它相当于i ++操作。
  4. getAndIncrement() – 以原子方式递增当前值并返回旧值。它相当于++ i 操作。
  5. decrementAndGet()- 原子地将当前值减1并在减量后返回新值。它等同于i– 操作。
  6. 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方法,循环操作对传进来的旧值和新值进行对比和更新,两值不一致就进行更新操作,从而达到自增的结果。




总结

以上就是今天要分享的内容,如果有什么不懂的地方或者有什么出错的地方,欢迎前来探讨,谢谢大家~

能够帮助你们解决问题是博主的荣幸,你们的支持是我创作的最大动力!:)欢迎关注点赞