一、前言

了解CAS,首先要清楚JUC,那么什么是JUC呢?JUC就是java.util.concurrent包的简称。它有核心就是CAS与AQS。CAS是java.util.concurrent.atomic包的基础,如AtomicInteger、AtomicBoolean、AtomicLong等等类都是基于CAS。

什么是CAS呢?全称Compare And Swap,比较并交换。CAS有三个操作数,内存值V,旧的预期值E,要修改的新值N。当且仅当预期值E和内存值V相同时,将内存值V修改为N,否则什么都不做。

二、实例

如果我们需要对一个数进行加法操作,应该怎样去实现呢?我们模拟多个线程情况下进行操作。

ThreadDemo.java 实现一个Runnable接口

package com.spring.security.test;

public class ThreadDemo implements Runnable {

	private int count = 0;

	@Override
public void run() {
for (int i = 0; i < 100; i++) {
addCount();
}
} private void addCount() {
count++;
} public int getCount() {
return count;
}
}

ThreadTest.java 创建线程池,提交10个线程执行,预期结果应该是1000

package com.spring.security.test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class ThreadTest {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(10);
ThreadDemo threadDemo = new ThreadDemo();
for (int i = 0; i < 10; i++) {
threadPool.submit(threadDemo);
}
threadPool.shutdown();
System.out.println(threadDemo.getCount());
}
}

运行结果:874 或其他,与预期结果不符合。

执行出来的结果并不是想象中的结果。这是为什么呢?这跟线程的执行过程有关。

所以我们需要在改变count,将值从高速缓冲区刷新到主内存后,让其他线程重新读取主内存中的值到自己的工作内存。

此时可以用volatile关键字。它的作用是保证对象在内存中的可见性。

修改ThreadDemo中的count字段

private volatile int count = 0;

此时执行结果:900 或其他,与预期结果不符合。

此时还是并未得出正确执行结果。为什么?听我细细道来。

线程安全主要体现在三个方面:

  • 原子性:提供了互斥访问,同一时刻只能有一个线程对它进行操作
  • 可见性:一个线程对主内存的修改可以及时的被其他线程观察到
  • 有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序

目前可见性已经实现了,缺少原子性的操作,因为同一时刻,多个线程对其操作,会将改动后的最新值读取到自己的工作内存进行操作,最终只能得到后一个执行线程操作的结果,所以相当于少了一步操作,就会造成数据的不一致。

此时可以使用JUC的Atomic包下面的类来进行操作。

Atomic类是使用CAS+volatile来实现原子性与可见性的

我们来改造一下TheadDemo.java中的实现方法

package com.spring.security.test;

import java.util.concurrent.atomic.AtomicInteger;

public class ThreadDemo implements Runnable {

	private AtomicInteger count = new AtomicInteger(0);

	@Override
public void run() {
for (int i = 0; i < 100; i++) {
// 递增
count.getAndIncrement();
}
} public int getCount() {
return count.get();
}
}

执行结果: 1000,符合预期值。

接下来我们来分析一下AtomicInteger类的源码:

private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset; static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
} private volatile int value;

Unsafe类是不安全的类,它提供了一些底层的方法,我们是不能使用这个类的。AtomicInteger的值保存在value中,而valueOffset是value在内存中的偏移量,利用静态代码块使其类一加载的时候就赋值。value值使用volatile,保证其可见性。

    /**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
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;
}

var1表示当前对象,var2表示value在内存中的偏移量,var4为增加的值。var5为调用底层方法获取value的值

compareAndSwapInt方法通过var1和var2获取当前内存中的value值,并与var5进行比对,如果一致,就将var5+var4的值赋给value,并返回true,否则返回false

由do while语句可知,如果这次没有设置进去值,就重复执行此过程。这一过程称为自旋。

compareAndSwapInt是JNI(Java Native Interface)提供的方法,可以是其他语言写的。

三、与synchronized比较

使用synchronized进行加法:

package com.spring.security.test;

public class ThreadDemo implements Runnable {

	private int count = 0;

	@Override
public void run() {
for (int i = 0; i < 100; i++) {
// 递增
synchronized (ThreadDemo.class) {
count++;
}
}
} public int getCount() {
return count;
}
}

运行结果: 1000,符合预期值。

使用synchronized和AtomicInteger都能得到预期结果,但是他们之间各有什么劣势呢?

synchronized是重量级锁,是悲观锁,就是无论你线程之间发不发生竞争关系,它都认为会发生竞争,从而每次执行都会加锁。

在并发量大的情况下,如果锁的时间较长,那将会严重影响系统性能。

CAS操作中我们可以看到getAndAddInt方法的自旋操作,如果长时间自旋,那么肯定会对系统造成压力。而且如果value值从A->B->A,那么CAS就会认为这个值没有被操作过,这个称为CAS操作的"ABA"问题。

Java中CAS 基本实现原理的更多相关文章

  1. Java中CAS 基本实现原理 和 AQS 原理

    一.前言了解CAS,首先要清楚JUC,那么什么是JUC呢?JUC就是java.util.concurrent包的简称.它有核心就是CAS与AQS.CAS是java.util.concurrent.at ...

  2. 从虚拟机指令执行的角度分析JAVA中多态的实现原理

    从虚拟机指令执行的角度分析JAVA中多态的实现原理 前几天突然被一个"家伙"问了几个问题,其中一个是:JAVA中的多态的实现原理是什么? 我一想,这肯定不是从语法的角度来阐释多态吧 ...

  3. Java中CAS原理详解

    在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. (2 ...

  4. Java中CAS原理分析(volatile和synchronized浅析)

    CAS是什么? CAS英文解释是比较和交换,是cpu底层的源语,是解决共享变量原子性实现方案,它定义了三个变量,内存地址值对应V,期待值E和要修改的值U,如下图所示,这些变量都是在高速缓存中的,如果两 ...

  5. Java中Synchronized的优化原理

    我们知道,从 JDK1.6 开始,Java 对 Synchronized 同步锁做了充分的优化,甚至在某些场景下,它的性能已经超越了 Lock 同步锁.那么就让我们来看看,它究竟是如何优化的. 原本的 ...

  6. java中jvm的工作原理

    首先我们安装了jdk和jre,但是jdk是为java软件开发工程师而使用的开发工具,我们运行java项目只要含有jre文件即可.对于jvm是内存分配的一块区域,我们知道,当我们开始使用java命令时, ...

  7. Java中CAS详解

    在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. (2 ...

  8. Java中HashMap的实现原理

    最近面试中被问及Java中HashMap的原理,瞬间无言以对,因此痛定思痛觉得研究一番. 一.Java中的hashCode和equals 1.关于hashCode hashCode的存在主要是用于查找 ...

  9. Java 中泛型的实现原理

    泛型是 Java 开发中常用的技术,了解泛型的几种形式和实现泛型的基本原理,有助于写出更优质的代码.本文总结了 Java 泛型的三种形式以及泛型实现原理. 泛型 泛型的本质是对类型进行参数化,在代码逻 ...

随机推荐

  1. docker,容器,编排,和基于容器的系统设计模式

    目录 从容器说起 背景 docker实现原理 编排之争 基于容器的分布式系统设计之道 单节点协作模式 Sidecar pattern(边车模式) Ambassador pattern(外交官模式) A ...

  2. 不用虚机不用Docker使用Azure应用服务部署ASP.NET Core程序

    一般我们写好了应用程序想要部署发布它,要么发布到物理机,要么发布到虚拟机,要么发布到容器来运行它.现在有了Azure应用服务,我们可以完全不用管这些东西,只管写好自己的代码,然后使用VisualStu ...

  3. noip复习——线性筛(欧拉筛)

    整数的唯一分解定理: \(\forall A\in \mathbb {N} ,\,A>1\quad \exists \prod\limits _{i=1}^{s}p_{i}^{a_{i}}=A\ ...

  4. Storcli64 工具操作指南

    1.1 介绍 storcli64可对LSIRAID卡基本操作进行管理,本文主要是对LSIRAID卡常使用到的命令进行介绍 1.2 基本语法 获取控制器号:storcli64 /call show al ...

  5. Maven报错Missing artifact jdk.tools:jdk.tools:jar:1.7

    1.eclipse中Maven项目的pom文件报错: 2.解决方法: 直接在pom.xml中加上一个依赖项目: <dependency>      <groupId>jdk.t ...

  6. 2.MongoDB 4.2副本集环境基于时间点的恢复

    (一)MongoDB恢复概述 对于任何数据库,如果要将数据库恢复到过去的任意时间点,否需要有过去某个时间点的全备+全备之后的重做日志. 接下来根据瑞丽航空的情况进行概述: 全备:每天晚上都会进行备份: ...

  7. Vue学习(十三)模版引擎算是预处理器吗?

    前言 今天在看vue-loader预处理器配置相关的内容,突然看到了Pug,然后有了一个疑问:模版引擎原来是预处理器吗? 答案是:YES 说明 这里重点讨论使用不同的js模板引擎作为预处理器, 下面示 ...

  8. 幂次方的四种快速取法(不使用pow函数)

    Pow(x, n) 方法一:暴力法 方法二:递归快速幂算法 方法三:迭代快速幂算法 方法四:位运算法 方法一:暴力法 思路 只需模拟将 x 相乘 n 次的过程. 如果 \(n < 0\),我们可 ...

  9. .NET Core + K8S + Apollo 玩转配置中心

    1.引言 Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性,适用于微服务配置管理 ...

  10. 5. JsonFactory工厂而已,还蛮有料,这是我没想到的

    少年易学老难成,一寸光阴不可轻.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习.关注公众号[ ...