AtomicInteger源码分析
- 问题背景
最近在看LinkedBlockingQueue看到了其中的count使用AtomicInteger修饰,之前也看过AtomicInteger的一些解释,也是似懂非懂的,今天深入的了解了其实现方式,学到了很多东西。
- 基础介绍
要对AtomicInteger有一个深入的认识,就必须要了解一下悲观锁和乐观锁。cpu是时分复用的,也就是把cpu的时间片,分配给不同的线程/进程轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生进程的切换。切换涉及到清空寄存器,缓存数据。然后重新加载新的thread所需数据。当一个线程被挂起时,加入到阻塞队列,在一定的时间或条件下,在通过notify(),notifyAll()唤醒回来。在某个资源不可用的时候,就将cpu让出,把当前等待线程切换为阻塞状态。等到资源(比如一个共享数据)可用了,那么就将线程唤醒,让他进入runnable状态等待cpu调度。这就是典型的悲观锁的实现。独占锁是一种悲观锁,synchronized就是一种独占锁,它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
但是,由于在进程挂起和恢复执行过程中存在着很大的开销。当一个线程正在等待锁时,它不能做任何事,所以悲观锁有很大的缺点。举个例子,如果一个线程需要某个资源,但是这个资源的占用时间很短,当线程第一次抢占这个资源时,可能这个资源被占用,如果此时挂起这个线程,可能立刻就发现资源可用,然后又需要花费很长的时间重新抢占锁,时间代价就会非常的高。
所以就有了乐观锁的概念,他的核心思路就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。在上面的例子中,某个线程可以不让出cpu,而是一直while循环,如果失败就重试,直到成功为止。所以,当数据争用不严重时,乐观锁效果更好。比如我们要说的AtomicInteger底层同步CAS就是一种乐观锁思想的应用。
CAS就是Compare and Swap的意思,比较并操作。很多的cpu直接支持CAS指令。CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
在CAS操作中,会出现ABA问题。就是如果V的值先由A变成B,再由B变成A,那么仍然认为是发生了变化,并需要重新执行算法中的步骤。有简单的解决方案:不是更新某个引用的值,而是更新两个值,包括一个引用和一个版本号,即使这个值由A变为B,然后为变为A,版本号也是不同的。
- AtomicInteger分析
Atomic包下类的理解:
Atomic包是Java.util.concurrent下的另一个专门为线程安全设计的Java包,包含多个原子操作类。这个包里面提供了一组原子变量类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。实际上是借助硬件的相关指令来实现的,不会阻塞线程(或者说只是在硬件级别上阻塞了)。可以对基本数据、数组中的基本数据、对类中的基本数据进行操作。原子变量类相当于一种泛化的volatile变量,能够支持原子的和有条件的读-改-写操作。—— 引自@chenzehe 的博客。
先来看一下AtomicInteger中getAndIncrement()方法的实现:
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
这个方法的做法为先获取到当前的 value 属性值,然后将 value 加 1,赋值给一个局部的 next 变量,然而,这两步都是非线程安全的,但是内部有一个死循环,不断去做compareAndSet操作,直到成功为止,也就是修改的根本在compareAndSet方法里面,compareAndSet()方法的代码如下:
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
compareAndSet 传入的为执行方法时获取到的 value 属性值,next 为加 1 后的值, compareAndSet所做的为调用 Sun 的 UnSafe 的 compareAndSwapInt 方法来完成,此方法为 native 方法,compareAndSwapInt 基于的是CPU 的 CAS指令来实现的。所以基于 CAS 的操作可认为是无阻塞的,一个线程的失败或挂起不会引起其它线程也失败或挂起。并且由于 CAS 操作是 CPU 原语,所以性能比较好。
下面以具体的例子分析下AtomicInteger的实现:
计数器(Counter),采用Java里比较方便的锁机制synchronized关键字,初步的代码如下:
public class Counter {
private int value; public synchronized int getValue() {
return value;
} public synchronized int increment() {
return ++value;
} public synchronized int decrement() {
return --value;
}
}
synchronized关键字是基于阻塞的锁机制,也就是说当一个线程拥有锁的时候,访问同一资源的其它线程需要等待,直到该线程释放锁,这也就我们前面所说的悲观锁。这里会有些问题:首先,如果被阻塞的线程优先级很高很重要怎么办?其次,如果获得锁的线程一直不释放锁怎么办?(这种情况是非常糟糕的)。还有一种情况,如果有大量的线程来竞争资源,那CPU将会花费大量的时间和资源来处理这些竞争(事实上CPU的主要工作并非这些),同时,还有可能出现一些例如死锁之类的情况,最后,其实锁机制是一种比较粗糙,粒度比较大的机制,相对于像计数器这样的需求有点儿过于笨重,因此,对于这种需求我们期待一种更合适、更高效的线程安全机制。
下面我们就以模拟CAS机制来实现Counter的例子:
CAS类:
public class SimpleCAS {
private volatile int value;
public synchronized int getValue(){
return value;
}
public synchronized boolean comperaAndSwap(int expectedValue,int newValue){
int oldValue = value;
if(oldValue == expectedValue){
value = newValue;
return true;
}else{
return false;
}
}
}
CASCounter类:
public class CASCounter {
private SimpleCAS cas;
public int getValue(){
return cas.getValue();
}
public int increment(){
int olevalue = cas.getValue();
for (; ;) {
if(cas.comperaAndSwap(olevalue, olevalue+1)){
return cas.getValue();
}
} }
}
上面的模拟不是CSA的真正实现,其实我们在语言层面是没有做任何同步的操作的,大家也可以看到源码没有任何锁加在上面,可它为什么是线程安全的呢?这就是Atomic包下这些类的奥秘:语言层面不做处理,我们将其交给硬件—CPU和内存,利用CPU的多处理能力,实现硬件层面的阻塞,再加上volatile变量的特性即可实现基于原子操作的线程安全。所以说,CAS并不是无阻塞,只是阻塞并非在语言、线程方面,而是在硬件层面,所以无疑这样的操作会更快更高效!
总结一下,AtomicInteger基于冲突检测的乐观并发策略。 可以想象,这种乐观在线程数目非常多的情况下,失败的概率会指数型增加。
参考内容:http://blog.csdn.net/zhangerqing/article/details/43057799,http://www.mamicode.com/info-detail-862009.html
AtomicInteger源码分析的更多相关文章
- AtomicInteger源码分析——基于CAS的乐观锁实现
AtomicInteger源码分析——基于CAS的乐观锁实现 1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时 ...
- JDK AtomicInteger 源码分析
@(JDK)[AtomicInteger] JDK AtomicInteger 源码分析 Unsafe 实例化 Unsafe在创建实例的时候,不能仅仅通过new Unsafe()或者Unsafe.ge ...
- 并发-AtomicInteger源码分析—基于CAS的乐观锁实现
AtomicInteger源码分析—基于CAS的乐观锁实现 参考: http://www.importnew.com/22078.html https://www.cnblogs.com/mantu/ ...
- 死磕 java并发包之AtomicInteger源码分析
问题 (1)什么是原子操作? (2)原子操作和数据库的ACID有啥关系? (3)AtomicInteger是怎么实现原子操作的? (4)AtomicInteger是有什么缺点? 简介 AtomicIn ...
- AtomicInteger源码分析——基于CAS的乐观锁实
1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生进程的切换.切换涉及 ...
- 【Java】CAS的乐观锁实现之AtomicInteger源码分析
1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生进程的切换.切换涉及 ...
- AtomicInteger 源码分析
AtomicInteger AtomicInteger 能解决什么问题?什么时候使用 AtomicInteger? 支持原子更新的 int 值. 如何使用 AtomicInteger? 1)需要被多线 ...
- JDK源码分析-AtomicInteger
AtomicInteger可以看做Integer类的原子操作工具类.在java.util.concurrent.atomic包下,在一些使用场合下可以取代加锁操作提高并发性.接下来就从几个方面来介绍: ...
- AtomicInteger原理&源码分析
转自https://www.cnblogs.com/rever/p/8215743.html 深入解析Java AtomicInteger原子类型 在进行并发编程的时候我们需要确保程序在被多个线程并发 ...
随机推荐
- nopcommerce3.3简洁版
从nopcommerce里面分离出了基础框架,包括了用户.新闻.单页面.投票等模块,可以作为快速开发asp.net mvc项目的方案,有兴趣的朋友可以下载看看,由于时间仓促可能会有一些多余的文件没有清 ...
- Remote Displayer for Android
应用截图: 作者:sunrain_hjb QQ:2157825357 Email:sunrain_hjb@aliyun.com 版本:1.0.188 ...
- Debian安装中文输入法
简单暴力: apt-get install ibus-pinyin 你也可以通过GUI下面到Synaptic Package Manager里面选中ibus-pinyin进行安装 安装完成后重启计算机 ...
- Linux 安装oracle客户端
环境: CentOS release 6.4 (Final) 一.下载文件 1.首先要查看oracle版本,对应的SQL如下: select * from V$version ; 2.根据oracl ...
- Android 动态加载 (三) PAK 详解
pak文件经常出现于游戏的安装目录中,其实pak文件是一种特殊的游戏压缩文件,用于压缩声音.图片等资料.由于pak文件专门针对游戏设计文件结构,pak文件就是将多个文件(图片.音乐.文本)打包为一个单 ...
- Android优化——UI优化(一)优化布局层次
优化布局层次 1.避免布局镶嵌过深(如下) <LinearLayout xmlns:android="http://schemas.android.com/apk/res/androi ...
- js中的return,return true,return false小结
return 函数执行到这句时会终结,并返回调用函数,而且把表达式的值作为函数的结果返回 return false 可以防止默认的事件行为.例如,默认情况下点击一个<a>元素,页面会跳转 ...
- [转]服务器自动化操作 RunDeck
From : http://www.oschina.net/p/rundeck/similar_projects?sort=view&lang=25 RunDeck 是用 Java/Grail ...
- Socket Programming in C#--Multiple Sockets
Now lets say you have two sockets connecting to either two different servers or same server (which i ...
- 在windows下python,pip,numpy,scipy,matplotlib的安装
系统:win7(64bit) 如果只需要安装python,执行步骤一就可以了,不用管后面.如果还需要其它的库,则只需要执行第二步,第一步可省略(因为在安装anaconda的时间,python就自动装好 ...