一、前言

了解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,符合预期值。

444

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

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

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

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

java cas登陆实例_Java CAS基本实现原理代码实例解析相关推荐

  1. java中thread实例_Java多线程并发执行demo代码实例

    主类:MultiThread,执行并发类 package java8test; import java.util.ArrayList; import java.util.List; import ja ...

  2. java io读取文件_java io读取文件操作代码实例

    这篇文章主要介绍了java io读取文件操作代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 主要分为字节读取和字符读取,字节读取可以一个一个 ...

  3. java 读excel 流_Java 读取excel 文件流代码实例

    这篇文章主要介绍了Java 读取excel 文件流代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 代码如下public static voi ...

  4. java byte数组操作_Java byte数组操纵方式代码实例解析

    字节数组的关键在于它为存储在该部分内存中的每个8位值提供索引(快速),精确的原始访问,并且您可以对这些字节进行操作以控制每个位. 坏处是计算机只将每个条目视为一个独立的8位数 - 这可能是你的程序正在 ...

  5. java实现语音发送,Java实现发送手机短信语音验证功能代码实例

    这篇文章主要介绍了java实现发送手机短信语音验证功能代码实例,www.cppcns.com文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 利用第三方 ...

  6. 基于java的圆通快递单号自动识别api接口代码实例

    一.产品介绍 快递单号识别,输入运单号自动识别物流公司,实时返回对应物流公司编码.查询单号时,返回的结果可能存在一个或多个物流公司编码,快递鸟大数据平台通过智能分析,实时更新单号库,保障物流公司编码准 ...

  7. java技术详解_Java反射技术详解及实例解析

    前言 相信很多人都知道反射可以说是Java中最强大的技术了,它可以做的事情太多太多,很多优秀的开源框架都是通过反射完成的,比如最初的很多注解框架,后来因为java反射影响性能,所以被运行时注解APT替 ...

  8. java代码读取excel文件_Java 读取excel 文件流代码实例

    这篇文章主要介绍了Java 读取excel 文件流代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 代码如下 public static vo ...

  9. java来电_JAVA来电显示接口调用代码实例

    代码描述:基于JAVA的来电显示接口调用代码实例 关联数据:手机固话来电显示 接口地址:http://www.juhe.cn/docs/api/id/72 import java.io.Buffere ...

最新文章

  1. deepstream-开放式实时服务器
  2. js中的各种宽高以及位置总结
  3. XFCE字体发虚的解决方法
  4. LibSvm python 调试实验
  5. Felgo之初体验--安装与运行
  6. 代理缓存服务器squid
  7. 前端问题记录1:debounce is not a function
  8. elasticsearch-head 谷歌插件以及安装和使用说明
  9. 李飞飞李佳“逆风翻盘”:谷歌全新AutoML产品发布,AI客服规模化商用
  10. SQLServer-Error Log
  11. ISA Server 2004 SP2新特性(上)
  12. 507 LOJ 「LibreOJ NOI Round #1」接竹竿
  13. python随机生成电话号码
  14. Linux交换分区 swap分区
  15. aid learning安装应用_极致安卓—Termux/Aid Learning安装宇宙最强VS Code
  16. css模糊遮罩效果_遮罩效果 css3
  17. 山东最新高级消防设施操作员考试真题及答案
  18. tenacity发生异常/失败/错误时重试retry机制,Python
  19. 企业内部流程运转受阻?免费又高效不愧是它
  20. 1185_SPC560B60L7_ADC0标准通道的Injected转换模式

热门文章

  1. 2023最新真心话坦白局系统源码/功能强大+价值万缘
  2. 周鸿祎反驳马化腾:即使战败 腾讯也将万劫不复
  3. LKT-L12 双界面读卡器
  4. flowable创建processEngine的流程
  5. 两数之和(java实现)
  6. 去耦电容的选择、容值计算和pcb布局布线详解
  7. java微信退款解密,微信退款-异步通知 报文解密
  8. 外贸邮件推广怎么统计维度
  9. Mellanox IB交换机SM HA
  10. 台式电脑一般价钱多少_台式电脑什么配置好一点(一般价位大概多少钱)