JUC中的原子类是依靠volatile变量和Unsafe类中的CAS函数实现的。

1. volatile变量的特性

  1. 内存可见性(当一个线程修改volatile变量的值后,另一个线程就可以实时看到此变量的更新值,也可以理解为敏感性)
  2. 禁止指令重排(位于volatile变量之前的变量执行先于volatile变量执行,volatile之后的变量执行在volatile变量之后)

2. CAS函数保证数据更新的原子性

CAS是Unsafe 类中定义的函数,它只有如下三种形式:

public final native boolean compareAndSwapInt(Object paramObject, long paramLong, int paramInt1, int paramInt2);

public final native boolean compareAndSwapLong(Object paramObject, long paramLong1, long paramLong2, long paramLong3);
public final native boolean compareAndSwapObject(Object paramObject1, long paramLong, Object paramObject2, Object paramObject3);

我们发现Unsafe类只提供了3种CAS方法:compareAndSwapInt、compareAndSwapLong和compareAndSwapObject,且都是native方法

AtomicBoolean 源码解析

public class AtomicBoolean implements java.io.Serializable {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset; static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicBoolean.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
} private volatile int value; public AtomicBoolean(boolean initialValue) {
value = initialValue ? 1 : 0;
}
public final boolean compareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}
...
}

从AtomicBoolean源码,发现它底层也是使用volatile类型的int 变量,跟AtomicInteger 实现方式一样,只不过是把Boolean转换成 0和1进行操作。
所以原子更新char、float和double变量也可以转换成int 或long来实现CAS的操作。

2.1 CAS的优点

1. CAS即为Compare-And-Swap,是一条CPU的原子指令。CAS操作需要输入两个数值,一个是读取到变量的旧值和一个新值,在操作期间首先比较变量的旧值与当前值是否一致,若一致则更新成新值,若不一致则重复上述操作直到成功为止。

2. CAS操作是原子性的,所以多线程并发中使用CAS更新数据,就可以不使用独占锁。独占锁是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止,乐观锁用到的机制就是CAS,如果不使用CAS,在高并发下,多线程同时修改一个变量的值我们需要synchronized加锁(可能有人说可以用Lock加锁,Lock底层的AQS也是基于CAS进行获取锁的)。

3.相信sql大家都熟悉,类似sql中的条件更新一样:update set id=3 from table where id=2。因为单条sql执行具有原子性,如果有多个线程同时执行此sql语句,只有一条能更新成功。

2.2 CAS的缺点

1.ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。
从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

2. 循环时间长开销大。自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。

【Java_多线程并发编程】JUC原子类——原子类中的volatile变量和CAS函数的更多相关文章

  1. 【Java_多线程并发编程】基础篇—Thread类中start()和run()方法的区别

    1. start() 和 run()的区别说明 start()方法: 它会启动一个新线程,并将其添加到线程池中,待其获得CPU资源时会执行run()方法,start()不能被重复调用. run()方法 ...

  2. 【Java_多线程并发编程】JUC原子类——AtomicLong原子类

    1. AtomicLong是基本原子类中的一种 AtomicLong是对长整形进行原子操作. 1.1 AtomicLong类的函数列表 // 构造函数 AtomicLong() // 创建值为init ...

  3. 【Java_多线程并发编程】JUC原子类——4种原子类

    根据修改的数据类型,可以将JUC包中的原子操作类可以分为4种,分别是: 1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ;2. 数组类型: Atom ...

  4. 【Java_多线程并发编程】基础篇—线程状态及实现多线程的两种方式

    1.Java多线程的概念 同一时间段内,位于同一处理器上多个已开启但未执行完毕的线程叫做多线程.他们通过轮寻获得CPU处理时间,从而在宏观上构成一种同时在执行的假象,实质上在任意时刻只有一个线程获得C ...

  5. 【Java_多线程并发编程】基础篇——synchronized关键字

    1. synchronized同步锁的原理 当我们调用某对象的synchronized方法或代码块时,就获取了该对象的同步锁.例如,synchronized(obj)就获取了“obj这个对象”的同步锁 ...

  6. 【Java_多线程并发编程】基础篇——线程状态扭转函数

    1. wait() sleep() yield() join()用法与区别 本文提到的当前线程是指:当前时刻,获得CPU资源正在执行的线程. 1.1 wait()方法 wait()方法定义在Objec ...

  7. Java 多线程并发编程一览笔录

    Java 多线程并发编程一览笔录 知识体系图: 1.线程是什么? 线程是进程中独立运行的子任务. 2.创建线程的方式 方式一:将类声明为 Thread 的子类.该子类应重写 Thread 类的 run ...

  8. Java 多线程并发编程

    导读 创作不易,禁止转载! 并发编程简介 发展历程 早起计算机,从头到尾执行一个程序,这样就严重造成资源的浪费.然后操作系统就出现了,计算机能运行多个程序,不同的程序在不同的单独的进程中运行,一个进程 ...

  9. Java 多线程 并发编程

    一.多线程 1.操作系统有两个容易混淆的概念,进程和线程. 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进程的地址空间是互相隔离的:进程拥有各种 ...

随机推荐

  1. performSegueWithIdentifier 不生效的解决办法

    相信很多人都会遇到这样的需求: APP 打开以后,判断用户是否登录,如果未登录,就跳转到登陆页. 今天我也遇到了这个需求,发现我封装的一个 `func checkLoginStatus()` 放在 ` ...

  2. 【书评】【不推荐】《TensorFlow:实战Google深度学习框架》(第2版)

    参考书 <TensorFlow:实战Google深度学习框架>(第2版) 这本书我老老实实从头到尾看了一遍(实际上是看到第9章,刚看完,后面的实在看不下去了,但还是会坚持看的),所有的代码 ...

  3. [题解](区间质数筛)POJ_2689 Prime Distance

    区间筛素数:先筛出1~sqrt(R)的素数,然后对于每个询问只要用这些素数筛掉区间内的合数即可. 几个细节:1.特判和1有关的一些情况 2.每次减去L偏移量,数组只开区间大小 3.POJ无法使用万能头 ...

  4. Linux (二)

    PS :显示系统进程 -a :显示所有进程(包括其他用户的进程) -u :用户以及其他详细信息 -x :显示没有控制终端的进程 -ef :显示所有 top :用于动态地监视进程活动与系统负载的信息 p ...

  5. 牛客寒假6-C.项链

    链接:https://ac.nowcoder.com/acm/contest/332/C 题意: 小B想给她的新项链染色. 现在有m种颜色,对于第i种颜色,小B有a_i单位的颜料,每单位颜料可以染项链 ...

  6. pycharm 虚拟环境virtualenv迁移到别的机器 无法读取包的问题

    将virtualenv迁移到别的机器时,发现pycharm 总是无法读取目录下所在的包,后来经过实验终于找到了问题所在: 将自己所建的虚拟环境目录下的orig-prefix.txt中保存的路径,改成新 ...

  7. Django 使用Paginator分页

    from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger subclass_s = models.subclas ...

  8. Unity加载AssetBundle的方法

    using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; usin ...

  9. WGET and CURL

    目录 WGET and CURL 对比 wget curl curl使用示例 WGET and CURL 对比 CURL 和WGET都可以用来下载文件,用法也类似:curl/wget [-option ...

  10. jsp get与post请求乱码问题

    乱码问题01:<%reques.setCharacterEncoding("utf-8");%> 02:get请求乱码 001.:String 编码之后的字符串 = n ...