在多线程开发中,我们常遇到的问题就是并发数据,怎么保证线程安全、怎么保证数据不重复。

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的更多相关文章

  1. 005-多线程-锁-JUC锁-LockSupport【使用、Unsafe、对比Object的wait、底层源码】

    一.概述 在Java多线程中,当需要阻塞或者唤醒一个线程时,都会使用LockSupport工具类来完成相应的工作.LockSupport定义了一组公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能 ...

  2. 008-多线程-锁-JUC锁-CyclicBarrier【让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行】

    一.概述 “循环栅栏”.大概的意思就是一个可循环利用的屏障. CyclicBarrier是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).因 ...

  3. 006-多线程-JUC线程池-并发测试程序

    一.java代码模拟并发 1.1.一次并发 单次并发测试 1.使用CountDownLatch 等待一个或多个线程一起执行 详细参看:007-多线程-锁-JUC锁-CountDownLatch-闭锁[ ...

  4. 深入理解Java多线程——线程池

    目录 为什么需要线程池 定义 ThreadPoolExecutor 工作队列workQueue 不同的线程池 Executor 线程池的工作原理 线程池生命周期 线程池增长策略 线程池大小的设置 线程 ...

  5. java.util.concurrent包详细分析--转

    原文地址:http://blog.csdn.net/windsunmoon/article/details/36903901 概述 Java.util.concurrent 包含许多线程安全.测试良好 ...

  6. CopyOnWriteArrayList你都不知道,怎么拿offer?

    前言 只有光头才能变强 前一阵子写过一篇COW(Copy On Write)文章,结果阅读量很低啊...COW奶牛!Copy On Write机制了解一下 可能大家对这个技术比较陌生吧,但这项技术是挺 ...

  7. java多线程系列笔记 目录

    基础篇 Java多线程系列 基础篇01 线程的状态 Java多线程系列 基础篇02 线程的创建和运行 Java多线程系列 基础篇03 线程的优先级和守护线程 Java多线程系列 基础篇04 线程中断 ...

  8. Java后端面经总结:拿下蚂蚁金服美团头条 offer 秘诀

    笔者在面过 猿辅导,去哪儿,旷视, 陌陌,头条, 阿里, 快手, 美团, 腾讯之后,除了收获一大堆面试问题,还思考到如何成为面试官眼中的”爱技术,爱思考,靠谱,有潜力候选人的”一些”套路”. 面试问题 ...

  9. CopyOnWriteArrayList(复制数组 去实现)

    一.Vector和SynchronizedList 1.1回顾线程安全的Vector和SynchronizedList 我们知道ArrayList是用于替代Vector的,Vector是线程安全的容器 ...

随机推荐

  1. 2019 HZNU Winter Training Day 13 Comprehensive Training

    A.Jongmah   CodeForces-1110D 题意:你在玩一个数字游戏,有一堆写在瓦片上的数字,希望你能组成最多的三元组(三个数字相同,或顺子). 这题用到的方法是动态规划.f[i][j] ...

  2. lightoj 1140 - How Many Zeroes?(数位dp)

    Jimmy writes down the decimal representations of all natural numbers between and including m and n, ...

  3. [USACO07OCT]障碍路线 & yzoj P1130 拐弯 题解

    题意 给出n* n 的图,A为起点,B为终点,* 为障碍,.可以行走,问最少需要拐90度的弯多少次,无法到达输出-1. 解析 思路:构造N * M * 4个点,即将原图的每个点分裂成4个点.其中点(i ...

  4. 010 深入理解Python语言

    目录 一.概述 二.计算机技术的演进 2.1 计算机技术的演进过程 三.编程语言的多样初心 3.1 编程语言有哪些? 3.2 不同编程语言的初心和适用对象 3.3 2018年以后的计算环境- 四.Py ...

  5. 012 模块1-turtle库的使用

    目录 一.概述 二.turtle库基本介绍 2.1 turtle库概述 2.2 标准库 2.3 turtle的原(wan)理(fa) 2.4 turtle的魅力 三.turtle绘图窗体布局 3.1 ...

  6. MD5字符串加密

    MD5字符串加密 Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护.该算法的文件号为RFC 132 ...

  7. Windows10 系统更新之后找不到输入法

    是因为 , 系统更新之后 , 系统自带的输入法没有更新好 , 过个一两天,系统会自动修复. 简单解决办法是 , Ctrl+Alt+delete 打开任务管理器, 在进程中,按CPU大小排序,找到输入法 ...

  8. 我真的不想再用 JPA 了

    在开发者的圈子里,没当说到一种技术好或者不好,都会引发激烈或者不激烈的争论,直到一个开发者出来说 PHP 是世界上最好的语言,大家伙儿才会纷纷退去继续写代码. 今天说 JPA 的问题不是想引发什么讨论 ...

  9. FreeSql (十二)更新数据时指定列

    var connstr = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;" + "Initia ...

  10. Java第三次作业第五题

    5. [问题描述]Fibonacci序列处理 从文件in.txt中读取第一个数作为Fibonacci数列的开始数n,读取第二个数,作为需要写入的数的个数m. 将从n开始的m个Fibonacci数列写入 ...