线程安全-JUC
在多线程开发中,我们常遇到的问题就是并发数据,怎么保证线程安全、怎么保证数据不重复。
1. volatile
volatile是一个java关键字,常用于在多线程中共享变量
volatile原理
每个thread都拥有自己的线程存储空间,并且什么时候将存储空间的数据同步到主内存中是不确定的。
volatile并不会给变量加上锁,性能上要比synchronized好,volatile可以保证线程在读取数据的时候都是直接读取内存(在线程A修改了变量的值后,会将变量写回到主内存中,同时其他线程会去主内存中获取变量的值)
volatile的优缺点
- 线程并发中保证变量的可见性(当这个变量发生变化时,对其他线程来说是可见的,线程中缓存的变量失效,线程获取到的永远是最新修改的值)
- volatile修饰的变量不允许线程内部cache缓存和重排序,保证了有序性
- volatile只能保证每次读取或修改时的原子性,i++ 不能保证原子性(只能保证单次的读取/修改原子性)
2. CAS (compare And Swap)
CAS 根据名字可以知道,先比较在交换值,jdk1.5后开始引入,通过和内存中的旧值比较,相同的话则修改成新的值
Compare And Swap原理
CAS指令执行时,只有当内存地址和修改值一致的时候才会将内存中的值修改为想要修改的值,否则则直接返回最新值不会做任何操作,但是会时不时的重试,直到更新了为止(自旋)。正因为这一点,CAS即使没有锁,也能及时发现其他线程对当前变量的改变。
CAS通过JNI(java本地调用)来实现,利用unsafe实现原子性操作(unsafe系统硬件级原子性操作,要么一起成功要么一起失败)
Compare And Swap的优缺点
优点:
- 不需要借助同步锁实现了线程之间的数据共享(依赖volatile将变量暴露)
- 从思想来说,synchronized属于悲观锁,悲观的认为并发情况严重,死死抓住资源不放;而CAS属于乐观锁,乐观的认为并发情况不怎么严重,再比较内存中的值不一致会反复重试
缺点
- CPU开销比较大,正因为CAS的自旋机制,会导致一个线程反复的尝试更新某个的值,带给CPU很大的压力
- 只能保证某个变量的原子性不能保证代码块的原子性
- ABA问题
ABA举证:
场景:假设ATM机底层用CAS实现的存取钱操作
小明账户还有100元,在ATM机上准备取出50元,假如ATM机bug或者鼓掌,小明触发了2次取钱操作;
这个时候如果只是触发了2次也没关系,在第一个请求处理时,根据CAS原理,对比账户余额和实际账户余额是一致的都是100,此时账户剩余50,并发的第二次取钱操作根据原理肯定是不会执行,但是会阻塞等待重试;这个时候是对账户没有任何影响的。
再假设如果在小明取钱的同时,小明家里给小明打了50元钱;这个时候还会是这样的情况?
在打钱线程执行的时候,发现账户余额期望值50与账户一致,打钱成功,账户余额:100;这还没完,
第2次取钱操作此时重试,发现期望值100与余额一致,余额扣款成功;
小明的账户经历了: 100(A) --> 50(B) --> 100(A) --> 50

ABA解决方法:在比较期望值和内存值的时候在增加比对内存地址值的版本号是否一致,线程每次在变更内存地址值的时候都会刷新版本。可以通过AtomicStampedReference类来实现版本的比较
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)));
}
CAS的应用
在java.util.concurrent并发包中
- AQS(AbstractQueuedSynchronizer),阻塞锁及一系列FIFO等待队列的框架,它底层都依赖对state这个变量做原子性操作来实现同步,修改state就有用到CAS等等...
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
- AtomicInteger、AtomicBoolean等都可以通过compareAndSet来更新值
在应用开发中
- 可以通过ato类来实现请求的并发操作
- 通过cas来原子性操作某个变量
线程安全-JUC的更多相关文章
- 005-多线程-锁-JUC锁-LockSupport【使用、Unsafe、对比Object的wait、底层源码】
一.概述 在Java多线程中,当需要阻塞或者唤醒一个线程时,都会使用LockSupport工具类来完成相应的工作.LockSupport定义了一组公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能 ...
- 008-多线程-锁-JUC锁-CyclicBarrier【让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行】
一.概述 “循环栅栏”.大概的意思就是一个可循环利用的屏障. CyclicBarrier是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).因 ...
- 006-多线程-JUC线程池-并发测试程序
一.java代码模拟并发 1.1.一次并发 单次并发测试 1.使用CountDownLatch 等待一个或多个线程一起执行 详细参看:007-多线程-锁-JUC锁-CountDownLatch-闭锁[ ...
- 深入理解Java多线程——线程池
目录 为什么需要线程池 定义 ThreadPoolExecutor 工作队列workQueue 不同的线程池 Executor 线程池的工作原理 线程池生命周期 线程池增长策略 线程池大小的设置 线程 ...
- java.util.concurrent包详细分析--转
原文地址:http://blog.csdn.net/windsunmoon/article/details/36903901 概述 Java.util.concurrent 包含许多线程安全.测试良好 ...
- CopyOnWriteArrayList你都不知道,怎么拿offer?
前言 只有光头才能变强 前一阵子写过一篇COW(Copy On Write)文章,结果阅读量很低啊...COW奶牛!Copy On Write机制了解一下 可能大家对这个技术比较陌生吧,但这项技术是挺 ...
- java多线程系列笔记 目录
基础篇 Java多线程系列 基础篇01 线程的状态 Java多线程系列 基础篇02 线程的创建和运行 Java多线程系列 基础篇03 线程的优先级和守护线程 Java多线程系列 基础篇04 线程中断 ...
- Java后端面经总结:拿下蚂蚁金服美团头条 offer 秘诀
笔者在面过 猿辅导,去哪儿,旷视, 陌陌,头条, 阿里, 快手, 美团, 腾讯之后,除了收获一大堆面试问题,还思考到如何成为面试官眼中的”爱技术,爱思考,靠谱,有潜力候选人的”一些”套路”. 面试问题 ...
- CopyOnWriteArrayList(复制数组 去实现)
一.Vector和SynchronizedList 1.1回顾线程安全的Vector和SynchronizedList 我们知道ArrayList是用于替代Vector的,Vector是线程安全的容器 ...
随机推荐
- poj 1753 Flip Game(暴力枚举)
Flip Game Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 52279 Accepted: 22018 Des ...
- hdu6351 Beautiful Now 杭电第五场 暴力枚举
Beautiful Now Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)T ...
- ZOJ 3872 Beauty of Array 连续子序列求和
Edward has an array A with N integers. He defines the beauty of an array as the summation of all dis ...
- redis.windows.conf配置详解
redis.windows.conf配置详解 转自:https://www.cnblogs.com/kreo/p/4423362.html # redis 配置文件示例 # 当你需要为某个配置项指定内 ...
- FreeSql (十一)更新数据 Where
var connstr = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;" + "Initia ...
- 利用HBuilder打包Vue开发的webapp为app
众所周知,前端开发完成的webapp只能运行在浏览器上,对运行环境有一定的限制,也就是除了浏览器其他的环境下不支持:那么现在如果有个需求是这样的呢?需要一套代码三端运行呢?三端运行(黑人脸??)是的, ...
- SpringSecurity自定义UsernamePasswordAuthenticationFilter
UsernamePasswordAuthenticationFilter介绍 UsernamePasswordAuthenticationFilter是AbstractAuthenticationPr ...
- web性能优化实践
一.SQL查询优化 1.循环中有多次查询sql,改为在循环外一次查询后再处理 2.循环多次插入,改为组装好数据后批量插入 3.梳理业务逻辑能一次查完的,绝不分多次查 4.索引用起来 5.分页查询 二. ...
- Netty源码分析 (十)----- 拆包器之LineBasedFrameDecoder
Netty 自带多个粘包拆包解码器.今天介绍 LineBasedFrameDecoder,换行符解码器. 行拆包器 下面,以一个具体的例子来看看业netty自带的拆包器是如何来拆包的 这个类叫做 Li ...
- Rocksdb基本用法
rocksdb 用法 rocksdb 介绍 RocksDB是使用C++编写的嵌入式kv存储引擎,其键值均允许使用二进制流.由Facebook基于levelDB开发, 提供向后兼容的levelDB AP ...