【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)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. ...
随机推荐
- Linux改中文乱码显示
Linux改中文乱码显示 可以使用locale命令,查看当前系统默认采用的字符集# locale在RedHat/CentOS系统下,记录系统默认使用语言的文件是/etc/ ...
- LVS+Keepalived 实现高可用负载均衡集群
LVS+Keepalived 实现高可用负载均衡集群 随着网站业务量的增长,网站的服务器压力越来越大?需要负载均衡方案!商业的硬件如 F5 ,Array又太贵,你们又是创业型互联公司如何有效 ...
- python 多进程处理 multiprocessing模块
前提: 有时候一个用一个进程处理一个列表中的每个元素(每个元素要传递到一个函数中进行处理),这个时候就要用多进程处理 1 现场案例: 我有一个[ip1,ip2,ip3,.......]这样的列表,我要 ...
- 震惊,某博主为吸引眼球拿出压箱底SQL总结,如果你没看那就吃亏了!(超级详细的SQL基础,你还不会的话就别学数据库了)
这里还有数据库相关的优质文章:快戳我,快戳我
- PyCharm 集成 SVN,检出、提交代码
1.安装 SVN,解决 SVN 目录中没有 svn.exe 问题 重新打开 TortoiseSVN 安装文件 选择 Modify 后在command line client tools 选项修改为 W ...
- C#基础之接口(6)
接口的定义:interface. 什么是接口? 接口,我的理解是接口是一种规范.就好比,一个生产数据线的厂商有很多,形状,外观都不一样,现在制定了一个规范那就是所有的数据线生产商都必须把产品外观形状都 ...
- 【XR-3】核心城市(树直径)
[XR-3]核心城市 这题真的难啊......... k个核心城市太麻烦,我们假设先找一个核心城市,应该放在哪里? \(任意取一个点,它的最远端是直径的端点.\) \(所以当这个点是直径的中点时,可以 ...
- D. Equalize the Remainders set的使用+思维
D. Equalize the Remainders set的学习::https://blog.csdn.net/byn12345/article/details/79523516 注意set的end ...
- java基础篇 之 异常丢失
我们看如下代码: @Slf4j public class Test { public static void main(String[] args) { try { try { test(); } f ...
- python执行脚本报错: Non-ASCII character '\xe4' in file yang.py on line 1, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details
这是因为我们在写python程序的时候,无论在注释里面出现中文,还是在代码里面出现中文,也就是说只要这个python程序里面有中文,我们就不能直接用python2来进行执行,如果安装是python3可 ...