JUC中原子操作类原理分析

简介: JUC中原子操作类原理分析

一、前言


java.util.concurrent 包里提供了一系列的原子性操作类,这些类都是使用CAS机制实现的,相当于使用锁实现原子性操作在性能上有了很大的提高。由于原子性操作类的原理都大致相同,所以本文主要讲解AtomicLong类的常用方法的分析。


二、原子变量操作类AtomicLong


AtomicLong是原子性递增或者递减类,其内部使用Unsafe来实现。


public class AtomicLong extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 1927816293512124184L;
  //(1).获取Unsafe类实例
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    //(2).存放变量value的偏移量
    private static final long valueOffset;
  //(3).native方法来判断JVM是否支持Long类型的无锁CAS
    static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
    private static native boolean VMSupportsCS8();
    static {
        try {
          //(4).获取value在AtomicLong中的偏移量
            valueOffset = unsafe.objectFieldOffset
                (AtomicLong.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
  //(5).实际变量值
    private volatile long value;
    public AtomicLong(long initialValue) {
        value = initialValue;
    }


由(1)处代码,获取Unsafe类对象的方法,是采用Unsafe.getUnsafe();获取到的,因为AtomicLong类同Unsafe类一样,是在rt.jar包下的,它也是是通过BootStrap类加载器进行加载的。可以在IDE中看下AutomicLong类的位置确认一下:


20201111202405245.png


由(1)处代码,获取Unsafe类对象的方法,是采用Unsafe.getUnsafe();获取到的,因为AtomicLong类同Unsafe类一样,是在rt.jar包下的,它也是是通过BootStrap类加载器进行加载的。可以在IDE中看下AutomicLong类的位置确认一下:


20201111202405245.png


科普一下,这里的类加载器涉及到JVM的知识。关于类加载器加载与所加载的类的对应关系见下图


20201111201733295.png

代码(2)和(4)中获取value变量在AtomicLong类中的偏移量。

代码(5)中的value变量被声明为volatile,这是为了在多线程下保证内存可见性,value是具体存放计数的变量。


二、AtomicLong中的主要方法


1、递增和递减操作

  //(6).调用unsafe的getAndAddLong方法,原子性设置value值为原始值+1,返回递增后的值
    public final long incrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
    }
    //(7).调用unsafe的getAndAddLong方法,原子性设置value值为原始值-1,返回递减后的值
    public final long decrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
    }
    //(8).调用unsafe的getAndAddLong方法,原子性设置value值为原始值+1,返回原始值
    public final long getAndIncrement() {
        return unsafe.getAndAddLong(this, valueOffset, 1L);
    }
   //(9).调用unsafe的getAndAddLong方法,原子性设置value值为原始值-1,返回原始值
    public final long getAndDecrement() {
        return unsafe.getAndAddLong(this, valueOffset, -1L);
    }

上述代码内部都是通过调用Unsafe的getAndAddLong方法来实现操作,getAndAddLong方法是原子性操作,第一个参数是AtomicLong实例的引用,第二个参数为value在AtomicLong中的偏移量,第三个参数是要设置的第二个变量的值。


简单看一下getAndAddLong 方法的实现

    public final long getAndAddLong(Object var1, long var2, long var4) {
        long var6;
        do {
            var6 = this.getLongVolatile(var1, var2);
        } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
        return var6;
    }

其中的getLongVolatile和compareAndSwapLong方法都是native方法,交给底层语言去实现。


2、boolean compareAndSet(long expect, long update)方法


看一下另外一个比较常用的compareAndSet方法:

  public final boolean compareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }


同样也是调用了compareAndSwapLong方法。如果原子变量中的value值等于expect,则使用update值更新value值并返回true,否则返回false。


3、多线程使用AtomicLong统计0的个数

package AtomicLongLearn;
import java.util.concurrent.atomic.AtomicLong;
/**
 * Created by Zhong Mingyi on 2020/11/12.
 */
public class AtomicLongTest {
    //(10)创建Long型原子计数器
    private static AtomicLong atomicLong = new AtomicLong();
    //(11)数据源
    private static Integer[] arr1 = new Integer[]{0,1,2,3,0,5,6,0,56,0};
    private static Integer[] arr2 = new Integer[]{10,1,2,3,0,5,6,0,56,0};
    public static void main(String[] args) throws InterruptedException {
        //(12)统计arr1中0的个数
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < arr1.length; i++) {
                if (arr1[i] == 0) {
                    atomicLong.incrementAndGet();
                }
            }
        });
        //(13)统计arr2中0的个数
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < arr2.length; i++) {
                if (arr2[i] == 0) {
                    atomicLong.incrementAndGet();
                }
            }
        });
        //(14)线程执行
        thread1.start();
        thread2.start();
        //(15)等待线程执行完毕
        thread1.join();
        thread2.join();
        System.out.println("两个数组中0的个数为:"+atomicLong.get());
    }
}

如上代码中的两个线程各自统计自所持数据中0的个数,每当找到一个0就会调用AtomicLong的原子性递增方法。


如果没有原子操作类,多线程下实现计数器需要使用比如synchronized的同步措施,会对性能有一定损耗;但原子操作类都使用基于CAS的非阻塞算法,因此会获取较好的性能。


但是在高并发情况下,AtomicLong还是会存在性能问题,JDK8提供了一个在高并发下性能更好的原子类,我们下一期再来揭晓~


三、总结


本期学习了java.util.concurrent 包里提供的原子性操作类之一——AtomicLong,它其中的主要方法均使用非阻塞算法CAS实现,这比起使用锁实现原子性操作在性能上有了很大的提升;然后我们使用了AtomicLong类来实现多线程下统计0的个数的需求,并以此更好来理解AtomicLong类;但是在高并发的下,AtomicLong类仍然有一定的性能瓶颈,JDK8中有更好的类去弥补它在高并发下性能的不足。

相关文章
|
9月前
|
安全
架构系列——通过ReentrantLock源码分析给对象上锁的原理
架构系列——通过ReentrantLock源码分析给对象上锁的原理
|
5天前
|
存储 安全 Java
多线程编程常见面试题讲解(锁策略,CAS策略,synchronized原理,JUC组件,集合类)(下)
多线程编程常见面试题讲解(锁策略,CAS策略,synchronized原理,JUC组件,集合类)(下)
45 0
|
5天前
|
存储 安全 Java
多线程编程常见面试题讲解(锁策略,CAS策略,synchronized原理,JUC组件,集合类)(上)
多线程编程常见面试题讲解(锁策略,CAS策略,synchronized原理,JUC组件,集合类)
38 0
|
5天前
|
存储 安全 Java
java多线程之原子操作类
java多线程之原子操作类
|
5天前
|
Java
高并发编程之JUC 三大辅助类和读写锁
高并发编程之JUC 三大辅助类和读写锁
28 1
|
8月前
|
Java
Java并发编程:使用Lock接口实现线程同步
Java是一种面向对象的编程语言,具有广泛应用于开发各种类型应用程序的能力。并发编程是Java中一个重要的主题,它允许多个任务同时执行,从而提高程序的性能和响应速度。
142 1
|
9月前
|
存储 安全 Java
JUC并发编程(JUC核心类、TimeUnit类、原子操作类、CASAQS)附带相关面试题
1.JUC并发编程的核心类,2.TimeUnit(时间单元),3.原子操作类,4.CAS 、AQS机制
42 0
|
12月前
|
存储 安全 算法
并发编程-03线程安全性之原子性(Atomic包)及原理分析
并发编程-03线程安全性之原子性(Atomic包)及原理分析
69 0
|
Java 开发者
JUC系列学习(三):ReentrantLock的使用、源码解析及与Synchronized的异同
`ReentrantLock`同`Synchronized`一样可以实现线程锁的功能,同样具有可重入性,除此之外还可以实现公平锁&非公平锁,其底层是基于`AQS`框架实现的。
|
Java 调度
锁原理分析系列——LockSupport工具类
锁原理分析系列——LockSupport工具类
80 0