首页
关于
友链
Search
1
wlop 4K 壁纸 4k8k 动态 壁纸
1,471 阅读
2
Nacos持久化MySQL问题-解决方案
932 阅读
3
Docker搭建Typecho博客
752 阅读
4
滑动时间窗口算法
728 阅读
5
Nginx反向代理微服务配置
699 阅读
生活
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
安全
开发工具
百度网盘
天翼网盘
阿里网盘
登录
Search
标签搜索
java
javase
docker
java8
springboot
thread
spring
分布式
mysql
锁
linux
redis
源码
typecho
centos
git
map
RabbitMQ
lambda
stream
少年
累计撰写
189
篇文章
累计收到
24
条评论
首页
栏目
生活
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
安全
开发工具
百度网盘
天翼网盘
阿里网盘
页面
关于
友链
搜索到
2
篇与
的结果
2022-04-20
AtomicStampedReference解决CAS中ABA问题
解决CAS的ABA问题CAS虽然高效的实现了原子性操作,但是也存在一些缺点,主要表现在以下三个方面。什么是ABA问题比如:线程1从主存中读取值A,另一个线程2也从主存中读取值A,此时线程2将主存值修改成了B,然后线程2又将主存值修改成了A,这时候线程1进行CAS操作发现内存中仍然是A,然后线程1操作成功,这是不正确的。如何解决ABA问题通过AtomicStampedReference的compareAndSet方法进行处理,这里的compareAndSet比一般的多了一个stamp的比较。源码如下: public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) { Pair<V> current = pair; return expectedReference == current.reference && expectedStamp == current.stamp && ((newReference == current.reference && newStamp == current.stamp) || casPair(current, Pair.of(newReference, newStamp))); }多了一个stamp的比较,stamp是每次更新来维护的。
2022年04月20日
178 阅读
0 评论
3 点赞
2022-04-19
并发及CAS
计算机架构CPU多级缓存为什么需要CPU cacheCPU的频率太快了,快到主存跟不上,这样在处理器时钟周期内,CPU常常需要等待主存,浪费资源。所以cache的出现,是为了缓解CPU和内存之间速度的不匹配问题(结构:cpu->cache->memory)。CPU cache有什么意义1)时间局部性:如果某个数据被访问,那么在不久的将来它很可能被再次访问2)空间局部性:如果某个数据被访问,那么与它相邻的数据很快也可能被访问;CPU 多级缓存一致性(MESI)CPU缓存4种状态M:被修改状态,指该缓存行只被缓存在该CPU的缓存中,并且是被修改过的。因此与主存中的数据是不一致的,该缓存行中的内存需要再未来的某个时间点写会主存。这个时间点是允许其它CPU读取主存中相应的内存之前,当这里面的值被写会主存之后该缓存行的状态会变成E独享状态。E:独享状态,指缓存行只被缓存在该CPU的缓存中,它是未被修改过的,是和主存的数据一致的,这个状态可以在任何时刻,当有其它CPU读取该内存时变成S共享状态。当CPU修改该缓存行内容时,状态变成M状态。S:共享状态,意味该缓存行可能被多个CPU缓存,并且各CPU缓存中的数据与主存中的数据一致,当有一个CPU修改该缓存行时,其它CPU中该缓存行,是可以被作废的,变成I无效状态。I:无效状态,代表缓存无效,可能有其它CPU修改了该缓存行。引起CPU缓存状态变化的4种操作local read:读本地缓存中的数据local write:将数据写到本地的缓存里面remote read:将主内中的数据读取到本地缓存中remote write:将本地缓存中数据写到主存里面MESI协议:CPU缓存数据有4种状态,引起CPU缓存转变的操作也有4种,因此本质就是要理解这16种操作状态的关系,可以通过下图表示:重点理解1、CPU缓存是为了减少CPU读写共享主存的次数。2、除了I状态,其它三种状态都是可以被读取的。3、只有再M和E状态才能写。4、当为S状态时,必须将该缓存行变为无效状态,一般通过广播方式完成,此时不允许不同的CPU同时修改该缓存行,即使修改该缓存行不同位置的数据也是不允许的,这里主要解决缓存一致性的问题。5、只有M和E状态可以写,如果是S状态需要变为I状态。6、一个处于M装的缓存行,需要时刻监听所有试图读该缓存行,相对有主存的操作,这种操作必须将缓存行写会到主存,并将状态变为S状态之前,被延迟执行。7、S状态的缓存行,监听所有试图将该缓存行无效或独享该缓存行的请求,并将缓存行变为无效。8、处于E状态的缓存行,需要监听其它读该缓存行的操作,一旦有该缓存行的操作,那么该缓存行需要变为S状态。因此对于M和E状态的数据总是精确的,和缓存行的状态是真正一致的。S状态可能是非一致的。如果一个缓存将处于S状态的数据作废了,另一个缓存实际上可能已经独享了该缓存行,但是该缓存不会把缓存行升迁为E状态,因为其它不会广播该作废的通知,同样由于缓存没有缓存该COPY的数量,因此也无法确定自己是否独享了该缓存行。E状态更像一种投机的优化,因为当一个CPU想修改一个处于S共享状态的缓存行,总线事务需要将所有copy该缓存行缓存的值变成I状态才行。修改E状态的CPU缓存不需要总线事务。JAVA内存模型抽象结构图JAVA内存模型8个操作8种同步操作lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定read(读取):作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中同步规则如果要把一个变量从主内存中复制到工作内存,就需要按顺寻地执行read和load操作,如果把变量从工作内存中同步回主内存中,就要按顺序地执行store和write操作。但Java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行不允许read和load、store和write操作之一单独出现不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。lock和unlock必须成对出现如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)compareAndSetInt举例一package com.yanxizhu.demo.concurrency.atomic; import com.yanxizhu.demo.concurrency.annotation.ThreadSafety; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicInteger; /** * @description: 线程安全 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/4/19 20:05 * @version: 1.0 */ @Slf4j @ThreadSafety public class DemoSemaphoreAndCountDownLatchAndAtomic { //用户数量 private static final int clientsTotal = 5000; //并发数量 private static final int concurrencyTotal = 200; //累加总和 private static AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newCachedThreadPool(); //信号量 final Semaphore semaphore = new Semaphore(clientsTotal); //闭锁 final CountDownLatch countDownLatch = new CountDownLatch(concurrencyTotal); for (int i = 0; i < clientsTotal; i++) { executorService.execute(()->{ try { semaphore.acquire(); add(); semaphore.release(); } catch (InterruptedException e) { log.error("出现错误:【{}】", e.getMessage()); } countDownLatch.countDown(); }); } countDownLatch.await(); executorService.shutdown(); log.info("累加结果为count=【{}】",count.get()); } /** * 累加 */ private static void add(){ //说明:incrementAndGet、getAndIncrement类似,i++与++i count.incrementAndGet(); count.getAndIncrement(); } }源码解析count.incrementAndGet();public final int incrementAndGet() { return U.getAndAddInt(this, VALUE, 1) + 1; }@HotSpotIntrinsicCandidate public final int getAndAddInt(Object o, long offset, int delta) { int v; do { v = getIntVolatile(o, offset); } while (!weakCompareAndSetInt(o, offset, v, v + delta)); return v; }weakCompareAndSetInt(o, offset, v, v + delta)@HotSpotIntrinsicCandidate public final boolean weakCompareAndSetInt(Object o, long offset, int expected, int x) { return compareAndSetInt(o, offset, expected, x); }compareAndSetInt(o, offset, expected, x)@HotSpotIntrinsicCandidate public final native boolean compareAndSetInt(Object o, long offset, int expected, int x);nativenative标识,说明是Java底层操作方法。重点说明@HotSpotIntrinsicCandidate /** * o: 当前对象,上面的count * offset: 当前值 (例如2+1),offset=2 * delta: (例如2+1),delta=1 **/ public final int getAndAddInt(Object o, long offset, int delta) { int v; do { //v:底层获取的值,如果没有其它线程修改,理论应该是2 v = getIntVolatile(o, offset); //重点: // 1)、当没有其它线程修改底层值时: // o这个对象,当前值offset=2与底层值v=2相等,则把底层值更新为(底层值v=2 + 增加值delta=1 = 最后就等于3) // // 2)、如果此有其它线程修改了底层值v(例如修改为v=4): // 此时低层值v=4与当前值offset=2不相等,则重新从count对象取出当前值offset=4, // 再判断当前值offset=4与底层值v=4相等,然后再讲底层值修改为(低层值v=4 + 增加值delta= 最后就登陆5), // 如果一直有其它线程修改底层值v,导致当前值offset与底层值v一直不相等,则一直循环上面的操作。 } while (!weakCompareAndSetInt(o, offset, v, v + delta)); return v; }CAS就是compareAndSet的缩写。底层值其实就是计算机的主内存,当前值就是JMM模型中工作内存值。特别说明JDK1.8后新增了LongAdder。AtomicLong:是基于 CAS 方式自旋更新的;LongAdder: 是把 value 分成若干cell,并发量低的时候,直接 CAS 更新值,成功即结束。并发量高的情况,CAS更新某个cell值和需要时对cell数据扩容,成功结束;更新失败自旋 CAS 更新 cell值。取值的时候,调用 sum() 方法进行每个cell累加。AtomicLong: 包含有原子性的读、写结合的api;LongAdder :没有原子性的读、写结合的api,能保证结果最终一致性。低并发:低并发场景AtomicLong 和 LongAdder 性能相似。高并发:高并发场景 LongAdder 性能优于 AtomicLong。
2022年04月19日
301 阅读
0 评论
3 点赞