【Java】手把手模拟CAS,瞬间理解CAS的机制
- 话不多少,先看个案例,【模拟100个用户,每个用户访问10次网站】”:
public class ThreadDemo1 {
//总访问量
private static int count = ;
//模拟访问的方法
public static void request() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep();//模拟耗时5s
count++;
}
public static void main(String[] args) throws InterruptedException {
long startTime = System.currentTimeMillis();
int threadSize = ;
CountDownLatch countDownLatch = new CountDownLatch(threadSize);
for (int i = ; i < ; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//模拟用户行为,每个用户访问10次网站
try {
for (int j = ; j < ; j++) {
request();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}
});
thread.start();
}
countDownLatch.await();
long endTime = System.currentTimeMillis();
System.out.println("访问用时:" + (endTime - startTime) + "ms");
System.out.println(count);
}
}
#结果1:
访问用时:73ms
#结果2:
访问用时:78ms
总之,结果基本都不会达到1000
- 分析一下问题出在哪呢?
【count++】 操作实际上是由三步来完成的(jvm执行引擎)
》获取count的值,记做A: A = count 》将A值+,得到B:B=A+ 》将B值赋给count

- 怎么解决结果不正确的问题呢?
对count++操作的时候,我们让多个线程排队处理,多个线程同时到达处理【request()方法】时,只能允许有一个线程进去操作,其他线程只能在外面等待(即串行化), 等到里面的线程处理完毕后,再让外面等待的线程进去一个,这样操作结果一定是争取的。
- 通常如何实现排队呢?
》synchronized关键字加锁
(详情可以了解《Synchronized底层加锁原理详解》:https://www.cnblogs.com/boluopabo/p/12907916.html)
》ReentrantLock可重入锁
- 那我们试一下【synchronized】加锁后的执行结果:
//我们试着在request方法前加一个synchronized 修饰
//模拟访问的方法
public synchronized static void request() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep();//模拟耗时5s
count++;
}
#结果:
访问用时:5884ms
虽然解决了线程的不安全问题,但是用却多了几十倍。
- 耗时太长的原因是什么呢?
程序中的request方法使用synchronized关键字修饰,保证了并发情况下,request方法同一时刻,只允许一个线程进入, request相当于串行执行了,count结果与预期一致,但耗时太长了
- 如何解决耗时太长问题呢?
文章最初我们简述了【count】的变化过程,这里我们再重复一遍,并延伸一下:
》获取count的值,记做A: A = count 》将A值+,得到B:B=A+ 》将B值赋给count 升级第三步的实现:
a.获取锁
b.获取以下count最新的值,记做LV
c.判断LV是否等于A,如果相等,则把B的值赋值给count,并返回true;否则返回false
d.释放锁
- 我们这里模拟一下上述升级第三步的实现场景:
package com.example.demo.thread; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; /**
* @author Code Farmer
* @date 2020/5/22 15:10
*/
public class ThreadDemo3 { //总访问量
//此处加volatile修改,保证可见性
private volatile static int count = ; //模拟访问的方法
public static void request() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep();//模拟耗时5s
// count++;
int expectCount;
//a.获取锁
//b.获取以下count最新的值,记做LV
while (!compareAndSwap((expectCount = getCount()), expectCount + )) { }
} /**
* @param expectCount count期望值
* @param newCount 需要给count赋予的新值
* @return 交换成功返回true;反之返回false
*/
public static synchronized boolean compareAndSwap(int expectCount, int newCount) {
// c.判断LV是否等于A,如果相等,则把B的值赋值给count,并返回true;否则返回false
// d.释放锁
if (expectCount == getCount()) {
count = newCount;
return true;
}
return false;
} private static int getCount() {
return count;
} public static void main(String[] args) throws InterruptedException {
long startTime = System.currentTimeMillis();
int threadSize = ;
CountDownLatch countDownLatch = new CountDownLatch(threadSize);
for (int i = ; i < ; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//模拟用户行为,每个用户访问10次网站
try {
for (int j = ; j < ; j++) {
request();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}
});
thread.start();
} countDownLatch.await();
long endTime = System.currentTimeMillis();
System.out.println("访问用时:" + (endTime - startTime) + "ms");
System.out.println(count);
} }
#结果:
访问用时:78ms
返回结果可以看到,性能可谓是爆炸式提升啊。
- 小结:在模拟CAS机制中,我们只在【将B值赋给count】这里加锁,从而减小了锁的粒度,以提高性能。
【Java】手把手模拟CAS,瞬间理解CAS的机制的更多相关文章
- 【Java】手把手理解CAS实现原理
先来看看概念,[CAS] 全称“CompareAndSwap”,中文翻译即“比较并替换”. 定义:CAS操作包含三个操作数 —— 内存位置(V),期望值(A),和新值(B). 如果内存位置的值与期望值 ...
- (白话理解)CAS机制
(白话理解)CAS机制 通过一段对话我们来了解cas用意 示例程序:启动两个线程,每个线程中让静态变量count循环累加100次. 最终输出的count结果是什么呢?一定会是200吗? 加了同步锁之后 ...
- Java 中的各种锁和 CAS + 面试题
Java 中的各种锁和 CAS + 面试题 如果说快速理解多线程有什么捷径的话,那本文介绍的各种锁无疑是其中之一,它不但为我们开发多线程程序提供理论支持,还是面试中经常被问到的核心面试题之一.因此下面 ...
- 无锁同步-JAVA之Volatile、Atomic和CAS
1.概要 本文是无锁同步系列文章的第二篇,主要探讨JAVA中的原子操作,以及如何进行无锁同步. 关于JAVA中的原子操作,我们很容易想到的是Volatile变量.java.util.concurren ...
- Java内存管理的进一步理解-模拟过程图解
Java内存管理的进一步理解-模拟过程图解--转载 java的内存管理分为: 1.堆内存:2.栈内存:3.方法区:4.本地方法区 /* 1:方法区 方法区存放装载的类数据信息包括: ...
- Java并发编程:什么是CAS?这回总算知道了
无锁的思想 众所周知,Java中对并发控制的最常见方法就是锁,锁能保证同一时刻只能有一个线程访问临界区的资源,从而实现线程安全.然而,锁虽然有效,但采用的是一种悲观的策略.它假设每一次对临界区资源的访 ...
- .NET:通过 CAS 来理解数据库乐观并发控制,顺便给出无锁的 RingBuffer。
背景 大多数企业开发人员都理解数据库乐观并发控制,不过很少有人听说过 CAS(我去年才听说这个概念),CAS 是多线程乐观并发控制策略的一种,一些无锁的支持并发的数据结构都会使用到 CAS,本文对比 ...
- 理解cas
前言 CAS(Compare and Swap),即比较并替换,实现并发算法时常用到的一种技术,Doug lea大神在java同步器中大量使用了CAS技术,鬼斧神工的实现了多线程执行的安全性. CAS ...
- 源码阅读 - java.util.concurrent (二)CAS
背景 在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. ...
随机推荐
- build.gradle 详解(一)
简述: 1) Java 开发中有两个大名鼎鼎的项目构建 ANT.Maven. 2) Google 推荐使用的 Android studio 是采用 Gradle 来构建项目.Gradle 是一个非常先 ...
- echarts自定义tooltip显示
使用echarts展示图形的时候,鼠标滑倒图像上,想展示除了系列名,数据名,数据值以外的数据,这时需要使用tooltip的fommater方式进行配置,另外对数据格式也有一定的要求. 如图所示:如果想 ...
- CSDN排名及积分规则
博客排行榜排名分值=个人Blog全部随笔与文章的阅读数之和+个人Blog全部评论数之和*10+个人所发表的评论数之和*50 CSDN博客积分计算规则: 1.每公布一篇原创或者翻译文章:可获得10分. ...
- django源码分析——静态文件staticfiles中间件
本文环境python3.5.2,django1.10.x系列 1.在上一篇文章中已经分析过handler的处理过程,其中load_middleware就是将配置的中间件进行初始化,然后调用相应的设置方 ...
- socket编程之并发回射服务器2
承接上文:socket编程之并发回射服务器 为了让服务器进程的终止一经发生,客户端就能检测到,客户端需要能够同时处理两个描述符:套接字和用户输入. 可以使用select达到这一目的: void str ...
- abp(net core)+easyui+efcore实现仓储管理系统——入库管理之十一(四十七)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- 为什么要学习微信小程序直播开发?最新的小程序直播介绍和优势分析!
小程序直播的介绍 “小程序直播”是微信提供给开发者的实时视频直播工具,包括直播管理端.主播端和观众端等模块,支持提供常用的用户互动和营销促销工具. 开发者只需在小程序中引入相关代码并在管理后台完成配置 ...
- 戴尔服务器ipmi报错
戴尔服务器ipmi配置完成,用浏览器打开报错 查看器已终止,网络已中断 原因:这个问题是java报错,用火狐打开报错 解决方法: 用IE打开就没问题,IE要用较高版本的,楼主的是win10-IE11
- Bootstrap初识
目录 概述 快速入门 响应式布局 CSS样式和JS插件 全局CSS样式 组件 插件 案例:黑马旅游网 概述 概念:一个前端开发的框架,Bootstrap是美国Twitter公司的设计师Mark Ott ...
- 【Hadoop离线基础总结】oozie调度shell脚本
目录 1.解压官方提供的调度案例 2.创建工作目录 3.拷贝任务模板到工作目录当中去 4.随意准备一个shell脚本 5.修改模板下的配置文件 6.上传调度任务到hdfs上面去 7.执行调度任务 1. ...