• 话不多少,先看个案例,【模拟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的机制的更多相关文章

  1. 【Java】手把手理解CAS实现原理

    先来看看概念,[CAS] 全称“CompareAndSwap”,中文翻译即“比较并替换”. 定义:CAS操作包含三个操作数 —— 内存位置(V),期望值(A),和新值(B). 如果内存位置的值与期望值 ...

  2. (白话理解)CAS机制

    (白话理解)CAS机制 通过一段对话我们来了解cas用意 示例程序:启动两个线程,每个线程中让静态变量count循环累加100次. 最终输出的count结果是什么呢?一定会是200吗? 加了同步锁之后 ...

  3. Java 中的各种锁和 CAS + 面试题

    Java 中的各种锁和 CAS + 面试题 如果说快速理解多线程有什么捷径的话,那本文介绍的各种锁无疑是其中之一,它不但为我们开发多线程程序提供理论支持,还是面试中经常被问到的核心面试题之一.因此下面 ...

  4. 无锁同步-JAVA之Volatile、Atomic和CAS

    1.概要 本文是无锁同步系列文章的第二篇,主要探讨JAVA中的原子操作,以及如何进行无锁同步. 关于JAVA中的原子操作,我们很容易想到的是Volatile变量.java.util.concurren ...

  5. Java内存管理的进一步理解-模拟过程图解

    Java内存管理的进一步理解-模拟过程图解--转载 java的内存管理分为: 1.堆内存:2.栈内存:3.方法区:4.本地方法区 /* 1:方法区      方法区存放装载的类数据信息包括:      ...

  6. Java并发编程:什么是CAS?这回总算知道了

    无锁的思想 众所周知,Java中对并发控制的最常见方法就是锁,锁能保证同一时刻只能有一个线程访问临界区的资源,从而实现线程安全.然而,锁虽然有效,但采用的是一种悲观的策略.它假设每一次对临界区资源的访 ...

  7. .NET:通过 CAS 来理解数据库乐观并发控制,顺便给出无锁的 RingBuffer。

    背景 大多数企业开发人员都理解数据库乐观并发控制,不过很少有人听说过 CAS(我去年才听说这个概念),CAS 是多线程乐观并发控制策略的一种,一些无锁的支持并发的数据结构都会使用到 CAS,本文对比 ...

  8. 理解cas

    前言 CAS(Compare and Swap),即比较并替换,实现并发算法时常用到的一种技术,Doug lea大神在java同步器中大量使用了CAS技术,鬼斧神工的实现了多线程执行的安全性. CAS ...

  9. 源码阅读 - java.util.concurrent (二)CAS

    背景 在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. ...

随机推荐

  1. Facebook发布神经蛋分离法,可从嘈杂环境中提取音视频

    分离混合分布是机器学习和信号处理的长期挑战,而Facebook近日提出的新方法似乎可以有效解决这一难题. 人类天生善于分离个别声音和视觉效果,例如在拥挤的鸡尾酒会上听到别人的声音,或者在动物穿过灌木丛 ...

  2. 基于opencv的人脸识别程序

    1. 解析opencv自带人脸识别源码(……/opencv-3.1.0/samples/cpp/facedetect.cpp) @ 操作系统:Ubuntu 15.04 OpenCV版本:3.1.0 # ...

  3. Jenkins 节点配置

    1.配置代理 系统管理---configure Global Security(全局安全设置)---Tcp port for inbound agents---指定端口 服务器防火墙中开放此端口(li ...

  4. Java笔记(day20-22)

    IO流: 输入流.输出流 字节流.字符流:为了处理文字数据方便而出现的对象. (其实这些对象的内部使用的还是字节流(因为文字最终也是字节数据,只不过,通过字节流读取了相对应的字节数,没有对这些字节直接 ...

  5. 物流配送中心管理系统(SSM+MYSQL)

    工程项目视频观看地址:www.toutiao.com/i6804066711… 本文首先对系统所涉及到的基础理论知识进行阐述,并在此基础上进行了系统分析.系统分析是平台开发的一个不可缺少的环节,为了能 ...

  6. 【FPGA篇章六】FPGA编译向导:详解编译预处理功能

    欢迎大家关注我的微信公众账号,支持程序媛写出更多优秀的文章 Verilog HDL语言和C语言一样也提供了编译预处理功能. Verilog HDL允许在程序中使用特殊的编译预处理语句. 在编译时,通常 ...

  7. 【漫画】JAVA并发编程 如何解决原子性问题

    原创声明:本文转载自公众号[胖滚猪学编程],转载务必注明出处! 在并发编程BUG源头文章中,我们初识了并发编程的三个bug源头:可见性.原子性.有序性.在如何解决可见性和原子性文章中我们大致了解了可见 ...

  8. {path:“ /”,expires:7}这一段是什么意思?

    1.创建会话cookie: $ .cookie('name','value'); 2.创建到期的cookie,然后7天: $ .cookie('name','value',{到期日:7}); 3.创建 ...

  9. HDU 2011 (水)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2011 题目大意:给你 m 个数,对于每个数,求前 n 项和,数列:1 - 1/2 + 1/3 - 1/ ...

  10. 03JAVA循环结构

    和JS\Python语句判断逻辑基本一致,不需要记录详细,只需要记录格式 一.for循环 for (初始化数据;判断语句:控制语句){ 循环体语句; } 二.while循环 初始化数据; while ...