【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)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. ...
随机推荐
- 多线程并行请求问题及SplashActivity预加载问题解决方案
1. 问题描述(一): 现有3个线程thread1, thread2, thread3.这3个线程是并发执行的,当着3个线程都执行完成以后,需要执行一个finish()事件. 1.1 实现方法: /* ...
- java之 惰性初始化
class Soap { private String s; Soap(){ System.out.println("Soap()"); s="Constructed&q ...
- 【抓包工具】tcpdump
tcpdump - dump traffic on a network 根据使用者的定义对网络上的数据包进行截获的包分析工具. tcpdump可以将网络中传送的数据包的“头”完全截获下来提供分析.它支 ...
- 听说你在从事前端开发?那这10个JavaScript的优化问题你不得不知道!
JavaScript的高效优化一直都是我们前端开发中非常重要的工作,也是很多开发人员无法做好的一部分内容,今天我总结了10个优化问题,大家可以参考来做优化,这其中很多问题都是大家经常遇到的哦. ==和 ...
- Spring PropertyPlaceholderConfigurer类载入外部配置
2019独角兽企业重金招聘Python工程师标准>>> 通常在Spring项目中如果用到配置文件时,常常会使用org.springframework.beans.factory.co ...
- The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 XKC's basketball team
XKC , the captain of the basketball team , is directing a train of nn team members. He makes all mem ...
- 目标检测之yolo源码分析
三.配置文件详解(config.py) import os # 数据集路径,和模型检查点路径 # # path and dataset parameter # DATA_PATH = 'data' # ...
- python(文件操作)
一.文件操作 1.tell()方法返回文件的当前位置,即文件指针当前位置. UTF-8 编码格式,一个中文占3个字符 GBK 编码格式,一个中文占2个字符 #写入前文件内容如下 "" ...
- 你真的会用Flutter日期类组件吗
Flutter系统提供了一些日期选择类组件,比如DayPicker.MonthPicker.YearPicker.showDatePicker.CupertinoDatePicker等,其中前4个为M ...
- Spring官网阅读(七)容器的扩展点(二)FactoryBean
在上篇文章中我们已经对容器的第一个扩展点(BeanFactoryPostProcessor)做了一系列的介绍.其中主要介绍了Spring容器中BeanFactoryPostProcessor的执行流程 ...