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

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. Codeforces#398 &767C. Garland 树形求子节点的和

    传送门 题意:在一个树上,问能否切两刀,使得三块的节点值的和相同. 思路: 由于这个总的节点和是不变的,每块的节点值和sum固定,dfs搜索,和等于sum/3,切.若不能分成三块(不能被3整除,-1) ...

  2. Oil Deposits UVA - 572

    The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSu ...

  3. 漫谈Spring Security 在Spring Boot 2.x endpoints中的应用(一)

    Spring Boot 2.x极大简化了默认的安全配置,并不是说有很多安全相关的配置,现在你只需要提供一个WebSecurityConfigurerAdapter继承类这样一个简单的操作,Spring ...

  4. win、mac 设置 php上传文件大小限制

    修改php.ini win平台WAMP修改 步骤 左键点击wamp 选择php 在弹出的窗口中选择php.ini 在打开的文件中进行修改(修改步骤如下) 修改完毕,保存并重启wamp mac  MAM ...

  5. Event Loop js 事件循环初理解

    浏览器环境 执行栈 所有的 JS 代码在运行是都是在执行上下文中进行的.执行上下文是一个抽象的概念,JS 中有三种执行上下文: 全局执行上下文,默认的,在浏览器中是 window 对象 函数执行上下文 ...

  6. HTTPS页面使用CNZZ统计代码,Chrome显示警告怎么办?

    很多站长会遇到一个问题,网站加入CNZZ的JS统计代码后,Chrome浏览器出现警告:阻止跨站解析器阻断脚本通过document.write调用(A parser-blocking, cross si ...

  7. Linux OOM-killer(内存不足时kill高内存进程的策略)

    OOM_killer是Linux自我保护的方式,当内存不足时不至于出现太严重问题,有点壮士断腕的意味 在kernel 2.6,内存不足将唤醒oom_killer,挑出/proc/<pid> ...

  8. FreeSql (二十四)Linq To Sql 语法使用介绍

    原本不支持 IQueryable 主要出于使用习惯的考虑,如果继承 IQueryable,编写代码的智能总会提示出现一堆你不想使用的方法(对不起,我有强迫症),IQueryable 自身提供了一堆没法 ...

  9. Flume介绍与安装

    搭建环境 部署节点操作系统为CentOS,防火墙和SElinux禁用,创建了一个shiyanlou用户并在系统根目录下创建/app目录,用于存放 Hadoop等组件运行包.因为该目录用于安装hadoo ...

  10. SQL手工注入进阶篇

    0.前言 上一篇我们介绍了SQL手工注入的流程以及步骤,但在实际的安全问题以及CTF题目中,查询语句多种多样,而且是肯定会对用户的输入进行一个安全过滤的,而这些过滤并不一定是百分百安全的,如何利用一些 ...