Java线程(十):CAS
前言
在Java并发包中有这样一个包,java.util.concurrent.atomic,该包是对Java部分数据类型的原子封装,在原有数据类型的基础上,提供了原子性的操作方法,保证了线程安全。以下以AtomicInteger为例,来看一下是怎样实现的。
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
public final int decrementAndGet() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return next;
}
}
以这两个方法为例,incrementAndGet方法相当于原子性的++i,decrementAndGet方法相当于原子性的--i(依据第一章和第二章我们知道++i或--i不是一个原子性的操作),这两个方法中都没有使用堵塞式的方式来保证原子性(如Synchronized),那它们是怎样保证原子性的呢,以下引出CAS。
Compare And Swap
CAS 指的是现代 CPU 广泛支持的一种对内存中的共享数据进行操作的一种特殊指令。这个指令会对内存中的共享数据做原子的读写操作。简介一下这个指令的操作过程:首先,CPU 会将内存中将要被更改的数据与期望的值做比較。然后,当这两个值相等时,CPU 才会将内存中的数值替换为新的值。否则便不做操作。最后,CPU 会将旧的数值返回。这一系列的操作是原子的。它们尽管看似复杂,但却是 Java 5 并发机制优于原有锁机制的根本。简单来说,CAS 的含义是“我觉得原有的值应该是什么,假设是,则将原有的值更新为新值,否则不做改动,并告诉我原来的值是多少”。(这段描写叙述引自《Java并发编程实践》)
简单的来说,CAS有3个操作数,内存值V,旧的预期值A,要改动的新值B。当且仅当预期值A和内存值V同样时,将内存值V改动为B,否则返回V。这是一种乐观锁的思路,它相信在它改动之前,没有其他线程去改动它;而Synchronized是一种悲观锁,它觉得在它改动之前,一定会有其他线程去改动它,悲观锁效率非常低。以下来看一下AtomicInteger是怎样利用CAS实现原子性操作的。
volatile变量
private volatile int value;
首先声明了一个volatile变量value,在第二章我们知道volatile保证了变量的内存可见性,也就是全部工作线程中同一时刻都能够得到一致的值。
public final int get() {
return value;
}
Compare And Set
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;// 注意是静态的 static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));// 反射出value属性,获取其在内存中的位置
} catch (Exception ex) { throw new Error(ex); }
} public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
比較并设置,这里利用Unsafe类的JNI方法实现,使用CAS指令,能够保证读-改-写是一个原子操作。compareAndSwapInt有4个參数,this - 当前AtomicInteger对象,Offset - value属性在内存中的位置(须要强调的是不是value值在内存中的位置),expect - 预期值,update - 新值,依据上面的CAS操作过程,当内存中的value值等于expect值时,则将内存中的value值更新为update值,并返回true,否则返回false。在这里我们有必要对Unsafe有一个简单点的认识,从名字上来看,不安全,确实,这个类是用于运行低级别的、不安全操作的方法集合,这个类中的方法大部分是对内存的直接操作,所以不安全,但当我们使用反射、并发包时,都间接的用到了Unsafe。
循环设置
public final int incrementAndGet() {
for (;;) {// 这样优于while(true)
int current = get();// 获取当前值
int next = current + 1;// 设置更新值
if (compareAndSet(current, next))
return next;
}
}
循环内,获取当前值并设置更新值,调用compareAndSet进行CAS操作,假设成功就返回更新至,否则重试到成功为止。这里可能存在一个隐患,那就是循环时间过长,总是在当前线程compareAndSet时,有还有一个线程设置了value(点子太被了),这个当然是属于小概率时间,眼下Java貌似还不能处理这样的情况。
缺点
Java线程(十):CAS的更多相关文章
- Java线程专栏文章汇总(转)
原文:http://blog.csdn.net/ghsau/article/details/17609747 JDK5.0之前传统线程 Java线程(一):线程安全与不安全 Java线程 ...
- Java线程专栏文章汇总
转载自 http://blog.csdn.net/ghsau/article/details/17609747 JDK5.0之前传统线程 Java线程(一):线程安全与不安全 J ...
- 并发编程(十二)—— Java 线程池 实现原理与源码深度解析 之 submit 方法 (二)
在上一篇<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法.这篇文章是接着上一篇文章 ...
- Java多线程和并发(十二),Java线程池
目录 1.利用Executors创建线程的五种不同方式 2.为什么要使用线程池 3.Executor的框架 4.J.U.C的三个Executor接口 5.ThreadPoolExecutor 6.线程 ...
- Java 线程总结(十四)
1.在异步任务进程中,一种常见的场景是,主线程提交多个异步任务,然后希望有任务完成就处理结果,并且按任务完成顺序逐个处理,对于这种场景,Java 并发包提供了一个方便的方法,使用 Completion ...
- Java线程和多线程(十二)——线程池基础
Java 线程池管理多个工作线程,其中包含了一个队列,包含着所有等待被执行的任务.开发者可以通过使用ThreadPoolExecutor来在Java中创建线程池. 线程池是Java中多线程的一个重要概 ...
- 深入浅出Java线程池:源码篇
前言 在上一篇文章深入浅出Java线程池:理论篇中,已经介绍了什么是线程池以及基本的使用.(本来写作的思路是使用篇,但经网友建议后,感觉改为理论篇会更加合适).本文则深入线程池的源码,主要是介绍Thr ...
- Java线程的概念
1. 计算机系统 使用高速缓存来作为内存与处理器之间的缓冲,将运算需要用到的数据复制到缓存中,让计算能快速进行:当运算结束后再从缓存同步回内存之中,这样处理器就无需等待缓慢的内存读写了. 缓 ...
- 细说进程五种状态的生老病死——双胞胎兄弟Java线程
java线程的五种状态其实要真正高清,只需要明白计算机操作系统中进程的知识,原理都是相同的. 系统根据PCB结构中的状态值控制进程. 单CPU系统中,任一时刻处于执行状态的进程只有一个. 进程的五种状 ...
随机推荐
- Spring 中拦截器与过滤器的区别
spring 中拦截器 与servlet 的filter 有相似之处.比如二者都是aop 编程思想的体现都能实现权限检查,日志记录等. 不同之处 使用范围不同 Filter 是Servlet 规定的. ...
- 转帖Jmeter中的几个重要测试指标释义
Aggregate Report 是 JMeter 常用的一个 Listener,中文被翻译为“聚合报告”.今天再次有同行问到这个报告中的各项数据表示什么意思,顺便在这里公布一下,以备大家查阅. 如果 ...
- C++ 面向对象学习1
#include "stdafx.h" #include <iostream> //不要遗漏 否则不能使用cout using namespace std; class ...
- 清除缓存、开启IO统计
SQL性能优化前期准备-清除缓存.开启IO统计 如果需要进行SQl Server下的SQL性能优化,需要准备以下内容: 一.SQL查询分析器设置: 1.开启实际执行计划跟踪. 2.每次执行需优化SQL ...
- 磁珠在PCB中的应用
1.磁珠的单位是欧姆,而不是亨特,这一点要特别注意.因为磁珠的单位是按照它在某一频率产生的阻抗来标称的,阻抗的单位也是欧姆.磁珠的 DATASHEET上一般会提供频率和阻抗的特性曲线图,一般以100M ...
- Delphi中使用TXMLDocument控件应注意的问题
今天写了一个类,其中用到了TXMLDocument控件.这个控件我是要动态生成的. 但是却遇到了非常奇怪的问题,下面分享一下 procedure TMainForm.Button1Click(Send ...
- systemd.service 中文手册
版权声明 本文译者是一位开源理念的坚定支持者,所以本文虽然不是软件,但是遵照开源的精神发布. 无担保:本文译者不保证译文内容准确无误,亦不承担任何由于使用此文档所导致的损失. 自由使用:任何人都可以自 ...
- HDU3966(树链剖分)
题目:Aragorn's Story 题意:给一棵树,并给定各个点权的值,然后有3种操作: I C1 C2 K: 把C1与C2的路径上的所有点权值加上K D C1 C2 K:把C1与C2的路径上的所有 ...
- Ghost.py 0.1b3 : Python Package Index
Ghost.py 0.1b3 : Python Package Index Ghost.py 0.1b3 Download Ghost.py-0.1b3.tar.gz Webkit based web ...
- webservice的讲解
Web Service实践之——开始XFire 一.Axis与XFire的比较 XFire是与Axis2 并列的新一代WebService平台.之所以并称为新一代,因为它: 1.支持一系列Web Se ...