JDK1.5提供的原子类原理及使用
JDK提供的原子类原理及使用
volatile只能保障可见性,不能保障原子性,如value++操作,就不是一个原子性操作,value++共分为以下三步操作(假如value的值是0):
- 1、取出value的值为0;
- 2、将value的值进行加一操作,得到一个新值为1;
- 3、将新值1再赋值给变量value。
假如线程1刚执行完了第二步,此时value的值依然为0,得到的新值为1,然后就轮到线程2执行。线程2执行第一步,取出value的值为0,再执行第二步,依然得到的新值为1,再执行第三步,将1赋值给value,执行完毕。此时线程1继续执行,又将1赋值给了value。所以在线程1和线程2执行完毕之后,执行了两次++操作,值本应该为2的value此时的值却为1,这时就出现了线程安全性问题。
解决的办法就是让非原子性的++操作变成一个原子性操作即可,也就是说线程2必须等线程1三个步骤都执行完毕之后才能执行,通常我们都会使用synchronized同步代码块来保障++操作的原子性及内存的可见性
public synchronized int getNext() {return value++; }
由于synchronized会导致线程上下文的切换,性能并不高,所以我们可以使用JDK提供的原子类的方式来保障内存的可见性及变量操作的原子性(注:原子类底层是volatile和CAS共同作用的结果)。
public class MainTest {private AtomicInteger value = new AtomicInteger(0);public int getNext() {// value++return value.getAndIncrement();}public static void main(String[] args) {MainTest mainTest = new MainTest();Runnable r = new Runnable() {@Overridepublic void run() {while (true) {String threadName = Thread.currentThread().getName();System.out.println(threadName + " " + mainTest.getNext());try {Thread.sleep(100);} catch (InterruptedException e) {}}}};new Thread(r).start();new Thread(r).start();} }
原子类源码路径(java.util.concurrent.atomic)
原子类主要有以下的几种类型:
原子更新基本类型(AtomicInteger、AtomicBoolean、AtomicLong)
AtomicInteger value = new AtomicInteger(0); value.getAndIncrement();
原子更新数组(AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray)
int [] s = {2,1,4,6}; AtomicIntegerArray a = new AtomicIntegerArray(s); // 将角标为2的值进行加1操作 a.getAndIncrement(2); // 将角标为2的值进行加10操作 a.getAndAdd(2, 10);
原子更新引用类型(AtomicReference)
// 对user这个引用的get和set,保障对引用的原子性操作 AtomicReference<User> user = new AtomicReference<>();
原子更新字段(AtomicIntegerFieldUpdater,注:更新的字段不能使private,同时字段必须使用volatile进行修饰)
// 对类字段的原子性操作 AtomicIntegerFieldUpdater<User> old = AtomicIntegerFieldUpdater.newUpdater(User.class, "old"); User user = new User(); System.out.println(old.getAndIncrement(user));
原子类实现原子性操作的原理
通过乐观锁(CAS,Compare And Swap)来实现,所谓乐观锁就是每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
假如我们想对一个int型的变量value进行加1操作,总体的流程如下:
- 1、先获取出value的值:int A = get();
- 2、对value值进行加1操作:int B = A + 1;
- 3、进行CAS操作:boolean flag = cas(A,B);
- 4、假如flag为true,则返回 B,假如flag为false,则重复执行第一步操作,直到flag为true
AtomicInteger类中的getAndIncrement代码大致实现如下:
public final int getAndIncrement() {int A, B;do {A = get();B = A + 1;} while (!compareAndSet(A, B));return B }
compareAndSet函数主要的操作如下:
1、将A值和内存中的V值进行比较,若A != V,则返回false;
2、若A == V,则将内存值更新为B,并返回true。
从以上的步骤中我们也可以看出,整个过程的关键在于compareAndSet函数必须是一个原子性的操作,然而CAS也称为比较和交换,从字面的意义上我们可以得知CAS操作至少需要两步才能完成,所以compareAndSet函数也必须通过加锁来保障原子性。
从JDK中的源码我们可以看到,compareAndSet并不是使用synchronized来保障原子性的,因为如果使用了synchronized的话,那么乐观锁将失去其自身的意义了。
compareAndSet函数调用的是unsafe.compareAndSwapInt方法。Unsafe类提供了硬件级别的原子操作,这个原子性的保证并不是靠java本身保证,而是靠一个更底层的与操作系统相关的特性实现(CPU锁),因此,JVM中的CAS的原子性是处理器保障的。
CAS存在着ABA的问题:
因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。
解决的办法就是引入版本号,每修改一次则版本号加1,只有版本号和值都和预期值一直,compareAndSet函数才返回true。
JDK1.5提供的原子类原理及使用相关推荐
- JDK提供的原子类原理与使用
原子更新基本类型 原子更新数组 原子更新抽象类型 原子更新字段 原子更新基本类型: package com.roocon.thread.t8; import java.util.concurrent. ...
- 高并发编程-07-JDK提供的原子类操作及原理
1,原子类介绍: 针对数据类型的操作,JDK提供的原子类来方便我们的线程安全控制. 所有的类保存在 java.util.concurrent.atomic 包中 基本数据类型 AtomicIntege ...
- cas无法使用_【漫画】CAS原理分析!无锁原子类也能解决并发问题!
本文来源于微信公众号[胖滚猪学编程].转载请注明出处 在漫画并发编程系统博文中,我们讲了N篇关于锁的知识,确实,锁是解决并发问题的万能钥匙,可是并发问题只有锁能解决吗?今天要出场一个大BOSS:CAS ...
- 并发笔记(八)JUC原子类以及线程池(Executors)
一.原子类 JUC中提供了针对一个变量的修改的简单原子操作,提供了原子类,相对于我们自己采用锁的方式实现来说,原子类的性能更好. 1.1原子类的底层实现原理理论:volatile+(循环的CAS) C ...
- 16.Atomic原子类体系概览
老王:小陈啊,从今天开始我们就要进入Atomic原子类系列的学习了,首先啊给你看一下JDK中提供给我们使用的原子类有哪些? 小陈:好啊,我记得JUC下面提供的原子类还是挺多的,所有的原子类的使用和底层 ...
- 6.Atomic原子类
Atomic原子类 JUC包下提供的原子类底层的实现原理基本都是差不多的,都是基于volatile和CAS操作来保证线程安全的. AtomicInteger.AtomicLong整型的原子类 Atom ...
- Java中常用的原子类
文章目录 一.什么是原子类 二.原子类的底层实现 三.常用的原子类 3.1.AtomicInteger与AtomicLong 3.2.LongAdder 四.原子类的性能测试 4.1.测试程序 4.2 ...
- JUC多线程:Atomic原子类与CAS原理
一.Atomic 原子类的原理: Atomic 原子操作类是基于无锁 CAS + volatile 实现的,并且类中的所有方法都使用 final 修饰,进一步保证线程安全.而 CAS 算法的具体实现方 ...
- Atomic原子类及原理
目录 1 前言 2 unsafe类对Atomic原子类的支持 3 AtomicInteger的内部实现 3.1 准备 3.2 读 3.3 写 4 CAS机制 4.1 基本操作数 4.2 例子 4.3 ...
最新文章
- 青龙羊毛——东方头条(搬砖,非原创)
- .net 使用 Aspose.Words 进行 Word替换操作
- 移动端geolocation插件+百度地图js获取地址
- 重装系统,配置CVS
- Java SSM框架之MyBatis3(六)MyBatis之参数传递
- Asp.net报错汇总:回发或回调参数无效
- linux下安装mysql-5.7.20
- Oracle系列--基础理论
- Keras的基本使用(1)--创建,编译,训练模型
- asp.net大型制造业进销存源码 c#源代码 bs 本系统 为ASP.NET C# WinForm源码,数据库为SQL Server。系统完全开源,
- php中的oop思想,oop_php oop思想_oop和ood
- P58-前端基础HTML-表格入门介绍
- java.sql.SQLException: The server time zone value ‘�й���ʱ��‘ is unrecognized or represents more tha
- 【20191025】考试
- 3D打印显神威:世界首颗3D打印卫星将入轨
- 【Pytorch】torch.nn.Conv1d()理解与使用
- java实现请求排队处理_【高并发】秒杀系统高并发请求排队处理
- 物料主数据 分类视图导入 BAPI_OBJCL_CREATE
- linux swap分区满了,Linux下如何释放内存、swap分区满了怎么办!
- Protel99 画层次原理图、多Part元件的绘制方法的一些心得记录
热门文章
- 自定义灵活 自动滚动的Dialog
- Generator 实现
- go 访问数据库mysql基础
- python tk text 自动换行_Python tkinter之Text
- MFC小笔记:TabCtrl父子窗口传递消息
- spark分区连接mysql_Spark数据存储和分区操作
- 【java】ssh the connection is not authenticated
- 【Spark】Spark的一个案例 Encountered removing nulls from dataset or using handleInvalid = “keep“ or “skip“
- 【ElasticSearch】Es 源码之 IndicesModule 源码解读
- 95-40-115-java.util.concurrent-线程-AbstractExecutorService