线程安全-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是线程安全的容器 ...
随机推荐
- 牛客小白月赛6 J 洋灰三角 数学
链接:https://www.nowcoder.com/acm/contest/136/J来源:牛客网 题目描述 洋灰是一种建筑材料,常用来筑桥搭建高层建筑,又称,水泥.混凝土. WH ...
- Codefroces 939 C Convenient For Everybody
939 C 题意:若干年以后地球会变成n个时区, 为了方便计时, 每个时区的时间从1:00开始到n:00点结束, 现在将要举行一场c赛, 每个时区内都有ai个人参加,并且比赛开始时间不早于当地时间s: ...
- selenium爬虫
Web自动化测试工具,可运行在浏览器,根据指令操作浏览器,只是工具,必须与第三方浏览器结合使用,相比于之前学的爬虫只是慢了一点而已.而且这种方法爬取的东西不用在意时候ajax动态加载等反爬机制.因此找 ...
- shell 的while语句
转 http://blog.chinaunix.net/uid-25880122-id-2901409.html while循环的格式 while expression do command co ...
- MATLAB之基本语法
常用命令 dir:列出当前目录下的所有文件 clc:清除命令窗 clear all:清除环境(从内存中清除所有变量) who:将内存中的当前变量以简单形式列出 close all: 关闭所有的 Fig ...
- Redis继续学习
1.Redis一共16个数据库 # Set the number of databases. The , you can select # a different one on a per-conne ...
- 搜索入门练习题9 LETTERS 题解
题目出处:<信息学奥赛一本通>第五章上机练习1 或者 POJ1154 题目描述 给出一个 \(R\times S\) 的大写字母矩阵,一开始你所处的位置在左上角,你可以向上下左右四个方向移 ...
- pip版本降级
使用命令:python -m pip install pip==8.1.1 其中, -m参数的解释 run library module as a script (terminates option ...
- Kubernetes 从懵圈到熟练:集群服务的三个要点和一种实现
作者 | 声东 阿里云售后技术专家 文章来源:Docker,点击查看原文. 以我的经验来讲,理解 Kubernetes 集群服务的概念,是比较不容易的一件事情.尤其是当我们基于似是而非的理解,去排查服 ...
- js中Math对象常用的属性和方法
1 Math对象 1.1定义:Math是js的一个内置对象,它提供了一些数学方法. 1.2特性:不能用构造函数的方式创建,无法初始化,只有静态属性和方法 1.3静态属性 1.3.1 Math.PI 圆 ...