Unsafe 和 Atomic 详解(上)

简介: 在JDK 5之后,Java类库中才开始使用CAS操作,该操作由sun.misc.Unsafe类里面的 compareAndSwapInt()和compareAndSwapLong()等几个方法包装提供。HotSpot虚拟机在内部对这些方法做了特殊处理,即时编译出来的结果就是一条平台相关的处理器CAS指令,没有方法调用的过程, 或者可以认为是无条件内联进去了。

在JDK 5之后,Java类库中才开始使用CAS操作,该操作由sun.misc.Unsafe类里面的 compareAndSwapInt()和compareAndSwapLong()等几个方法包装提供。HotSpot虚拟机在内部对这些方法做了特殊处理,即时编译出来的结果就是一条平台相关的处理器CAS指令,没有方法调用的过程, 或者可以认为是无条件内联进去了。不过由于Unsafe类在设计上就不是提供给用户程序调用的类(Unsafe::getUnsafe()的代码中限制了只有启动类加载器(Bootstrap ClassLoader)加载的Class才能访问它),因此在JDK 9之前只有Java类库可以使用CAS,譬如J.U.C包里面的整数原子类,其中的 compareAndSet()和getAndIncrement()等方法都使用了Unsafe类的CAS操作来实现。而如果用户程序也有用CAS操作的需求,那要么就采用反射手段突破Unsafe的访问限制,要么就只能通过Java类库API来间接使用它。直到JDK 9之后,Java类库才在VarHandle类里开放了面向用户程序使用的CAS操作。


Unsafe 实现 CAS


核心 API


  1. compareAndSwapObject


  1. compareAndSwapInt


  1. compareAndSwapInt


原子变量提供的原子性来自CAS操作,CAS来自Unsafe,然后由CPU的 cmpxchg 指令来保证。


Atomic 工具包


Atomic 工具类分为一下几类


  • 普通原子类型:提供对boolean、int、long和对象的原子性操作。


  • AtomicBoolean
  • AtomicInteger
  • AtomicLong
  • AtomicReference


  • 原子类型数组:提供对数组元素的原子性操作。


  • AtomicLongArray
  • AtomicIntegerArray
  • AtomicReferenceArray


  • 原子类型字段更新器:提供对指定对象的指定字段进行原子性操作。


  • AtomicLongFieldUpdater
  • AtomicIntegerFieldUpdater
  • AtomicReferenceFieldUpdater


  • 带版本号的原子引用类型:以版本戳的方式解决原子类型的ABA问题。


  • AtomicStampedReference
  • AtomicMarkableReference


  • 原子累加器(JDK1.8):AtomicLong和AtomicDouble的升级类型,专门用于数据统计,性能更高。


  • DoubleAccumulator
  • DoubleAdder
  • LongAccumulator
  • LongAdder


使用案例


多线程累加


下面是创建 1000 个线程分别对 total 进行累加,由于没有对 total 做任何线程安全操作所以我们打印的结果一定是小于或者等于 1000000


public class AtomicTest {
    public static int total = 0;
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch downLatch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    total++;
                }
                downLatch.countDown();
            }).start();
        }
        downLatch.await();
        System.out.println(total);
    }
}
// 结果输出
// 999843


通过 AtomicInteger 实现


我们可以通过 integer 的原子类,AtomicInteger 类来进行自增操作,然后调用 getAndIncrement() 方法,保证每次都是原子操作。最终的结果输出:1000000。


public class AtomicTest2 {
    public static AtomicInteger total = new AtomicInteger(0);
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch downLatch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    total.getAndIncrement();
                }
                downLatch.countDown();
            }).start();
        }
        downLatch.await();
        System.out.println(total.get());
    }
}
// 结果输出
// 1000000


再来看看 getAndIncrement 方法它主要是做了哪些操作呢 ?


public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}
// 然后调用 unsafe 类的本地方法
// unsafe.getAndAddInt 方法
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    return var5;
}


相关文章
|
Linux 数据安全/隐私保护
Linux中普通用户使用sudo命令提示lin is not in the sudoers file. This incident will be reported.
Linux中普通用户使用sudo命令提示lin is not in the sudoers file. This incident will be reported.
|
7月前
|
人工智能 负载均衡 Java
Spring AI Alibaba 发布企业级 MCP 分布式部署方案
本文介绍了Spring AI Alibaba MCP的开发与应用,旨在解决企业级AI Agent在分布式环境下的部署和动态更新问题。通过集成Nacos,Spring AI Alibaba实现了流量负载均衡及节点变更动态感知等功能。开发者可方便地将企业内部业务系统发布为MCP服务或开发自己的AI Agent。文章详细描述了如何通过代理应用接入存量业务系统,以及全新MCP服务的开发流程,并提供了完整的配置示例和源码链接。未来,Spring AI Alibaba计划结合Nacos3的mcp-registry与mcp-router能力,进一步优化Agent开发体验。
2575 14
|
10月前
|
监控 前端开发 Java
SpringBoot集成Tomcat、DispatcherServlet
通过这些配置,您可以充分利用 Spring Boot 内置的功能,快速构建和优化您的 Web 应用。
823 21
|
数据可视化
Echarts动态数据可视化学习(2)柱状图和折线图的动态数据更新
Echarts动态数据可视化学习(2)柱状图和折线图的动态数据更新
490 0
|
NoSQL Unix MongoDB
MongoDB-系统时钟跳变引发的风波
MongoDB-系统时钟跳变引发的风波
|
Java
java阿里云企业邮箱发送邮件
java阿里云企业邮箱发送邮件
4052 0
|
负载均衡 前端开发 Java
11-微服务技术栈(基础):Gateway服务网关
微服务中另一重要组件:网关 进行了实战性演练,网关作为分布式架构中的重要中间件,不仅承担着路由分发(重点关注Path规则配置),同时可根据自身负载均衡策略,对多个注册服务实例进行均衡调用。本节我们借助GateWay实现的网关只是技术实现的方案之一,后续大家可能会接触像:Zuul、Kong等,其实现细节或有差异,但整体目标是一致的。
7345 1
|
存储 弹性计算 数据库
在沙特,阿里云和沙特电信携手提供云计算服务
在沙特,阿里云和沙特电信携手提供云计算服务
951 0
|
缓存 搜索推荐 算法
玩转Python编程:打造自己的编程王国
如果你想自学 Python 编程语言,那么你来对了地方!Python 是一种非常流行的编程语言,具有广泛的应用场景,从 Web 开发到数据科学都能发挥作用。Python 也因为其简单易学和强大的功能而备受欢迎。