一、前言

了解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 实现_Java中CAS 基本实现原理相关推荐

  1. java 求正割_Java中AQS基本实现原理

    一.AQS概述 AQS全名AbstractQueuedSynchronizer,意为抽象队列同步器,JUC(java.util.concurrent包)下面的Lock和其他一些并发工具类都是基于它来实 ...

  2. Java并发编程包中atomic的实现原理

    转载自   Java并发编程包中atomic的实现原理 这是一篇来自粉丝的投稿,作者[林湾村龙猫]最近在阅读Java源码,这一篇是他关于并发包中atomic类的源码阅读的总结.Hollis做了一点点修 ...

  3. java 锁定界面_Java中的锁

    java中的锁遵循不同的分类方法,太多了,乐观锁/悲观锁,可重入锁/不可重入锁,有些第一遇到的话,可能还有点懵.刚好周末有时间学习下,总结和梳理下. 一总述 总的来说对java的锁有以下七种分类方法: ...

  4. java mod %区别_Java中 % 与Math.floorMod() 区别详解

    %为取余(rem),Math.floorMod()为取模(mod) 取余取模有什么区别呢? 对于整型数a,b来说,取模运算或者取余运算的方法都是: 1.求 整数商: c = a/b; 2.计算模或者余 ...

  5. java show过时_Java中show() 方法被那个方法代替了? java编程 显示类中信

    你说的show是swing里的吧,在老版本中Component这个超类确实有show这个方法,而且这个方法也相当有用,使一个窗口可见,并放到最前面.在jdk5.0中阻止了这个方法,普遍用setVisi ...

  6. java判断类型_Java中类型判断的几种方式 - 码农小胖哥 - 博客园

    1. 前言 在Java这种强类型语言中类型转换.类型判断是经常遇到的.今天就细数一下Java中类型判断的方法方式. 2. instanceof instanceof是Java的一个运算符,用来判断一个 ...

  7. java 序列化实例_Java中的序列化与反序列化实例

    创建的字节流与平台无关.因此,在一个平台上序列化的对象可以在另一个平台上反序列化. 为了使Java对象可序列化,我们实现java.io.Serializable可序列化接口. ObjectOutput ...

  8. java中session的作用_java中session的工作原理是什么?和Cookies有何区别?

    现在大家学习的东西和以前大不相同了,越来越多的人倾向于去学习高新技术以获得更好的发展.java是很多人的第一选择.java中的知识还是很多的,今天就来为大家介绍一下. 首先来说一下java中sessi ...

  9. java 数据类型分为_JAVA中分为基本数据类型及引用数据类型

    byte:Java中最小的数据类型,在内存中占8位(bit),即1个字节,取值范围-128~127,默认值0 short:短整型,在内存中占16位,即2个字节,取值范围-32768~32717,默认值 ...

最新文章

  1. lua学习笔记之协程
  2. 【深度学习】卷积神经网络(CNN)详解
  3. C++突破private的方案
  4. #define va_arg(AP, TYPE)
  5. 2015年《大数据》高被引论文Top10文章No.3——我国政府数据开放现状和保障机制...
  6. 单片机原理及应用C语言实验,《单片机原理及应用》实验指导书.doc
  7. String常用方法有哪些?在工作中使用过哪些?
  8. 用于热水器行业气密性检测的五款快速密封接头
  9. OPTEE:CA-TA会话的创建(二)
  10. 关于微信数据库文件夹与资源文件夹名称的问题
  11. 情侣的网站代码java_GitHub - ByronCui/lovers-website: 程序员的情侣网站 (programmer's website of lovers)...
  12. leetcode1438
  13. w8系统桌面没有计算机图标,不见了win8系统桌面图标怎么办
  14. 对爱词霸(iciba)生词本功能的一些建议
  15. poi 读取excel合并单元格两种方式
  16. 云计算介绍之云计算服务器
  17. 100%成功王者荣耀战区修改(亲测)
  18. 酿酒知识-年会喝酒?你打算如何安排你的酒桌呢?
  19. java计算机毕业设计疫情物质管理系统源码+数据库+lw文档+系统+部署
  20. Reddit的发帖注意事项和技巧

热门文章

  1. Vue element el-select 设置默认选中
  2. 对抗攻击3——BIM(Basic Iterative Method)
  3. 教你用OpenCV和Python实现手掌检测和手掌计数
  4. android录音程序,使用MediaRecorder编写Android 录音程序
  5. 如何一键将大批量的护照图片转为excle表格?
  6. SAP发出商品业务配置
  7. 微信公众平台升级9大高级接口功能解读
  8. pyecharts echarts 更改数据集
  9. 不属于简历撰写技巧原则的是_撰写有效的技术简历的7个技巧
  10. Ubuntu16.04下Python程序出现错误qt.qpa.plugin: Could not load the Qt platform plugin xcb解决方法