lesson8:AtomicInteger源码解析及性能分析
AtomicInteger等对象出现的目的主要是为了解决在多线程环境下变量计数的问题,例如常用的i++,i--操作,它们不是线程安全的,AtomicInteger引入后,就不必在进行i++和i--操作时,进行加锁操作,在我们日常工作中,有很多业务场景需要在多线程环境下进行变量的计数:订单数统计、访问量统计、累计相应时长统计等。
demo 源码:https://github.com/mantuliu/javaAdvance
下面我们先分析一下AtomicInteger的源代码。通过源码分析我们知道,AtomicInteger的核心就是一个CAS算法(CompareAndSwap),比较并交换算法,此算法是由unsafe的底层代码实现,它是一个原子的操作,原理就是:如果内存中的实际值与update值相同,则将实际值更新为expect值,反之则返回失败,由上层系统循环获取实际值后,再次调用此CAS算法:
/*
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/ /*
*
*
*
*
*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/ package java.util.concurrent.atomic;
import sun.misc.Unsafe; /**
* An {@code int} value that may be updated atomically. See the
* {@link java.util.concurrent.atomic} package specification for
* description of the properties of atomic variables. An
* {@code AtomicInteger} is used in applications such as atomically
* incremented counters, and cannot be used as a replacement for an
* {@link java.lang.Integer}. However, this class does extend
* {@code Number} to allow uniform access by tools and utilities that
* deal with numerically-based classes.
*
* @since 1.5
* @author Doug Lea
*/
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;//value值的偏移地址 static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
} private volatile int value;//实际的值 /**
* Creates a new AtomicInteger with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicInteger(int initialValue) {
value = initialValue;//初始化
} /**
* Creates a new AtomicInteger with initial value {@code 0}.
*/
public AtomicInteger() {
} /**
* Gets the current value.
*
* @return the current value
*/
public final int get() {
return value;//返回value值
} /**
* Sets to the given value.
*
* @param newValue the new value
*/
public final void set(int newValue) {
value = newValue;//设置新值,因为没有判断oldvalue,所以此操作是非线程安全的
} /**
* Eventually sets to the given value.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);//与set操作效果一样,只是采用的是unsafe对象中通过偏移地址来设置值的方式
} /**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
public final int getAndSet(int newValue) {//原子操作,设置新值,返回老值
for (;;) {
int current = get();
if (compareAndSet(current, newValue))//通过CAS算法,比较current的值和实际值是否一致,如果一致则设置为newValue
return current;
}
} /**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
} /**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
* and does not provide ordering guarantees, so is only rarely an
* appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @return true if successful.
*/
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
} /**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {//i++操作
for (;;) {
int current = get();//获取当前值
int next = current + 1;//当前值+1
if (compareAndSet(current, next))//比较current值和实际的值是否一致,如不一致,则继续循环
return current;
}
} /**
* Atomically decrements by one the current value.
*
* @return the previous value
*/
public final int getAndDecrement() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return current;
}
} /**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the previous value
*/
public final int getAndAdd(int delta) {//例如:当我们统计接口的响应时间时,可以利用此方法
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return current;
}
} /**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
} /**
* Atomically decrements by one the current value.
*
* @return the updated value
*/
public final int decrementAndGet() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return next;
}
} /**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the updated value
*/
public final int addAndGet(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return next;
}
} /**
* Returns the String representation of the current value.
* @return the String representation of the current value.
*/
public String toString() {
return Integer.toString(get());
} public int intValue() {
return get();
} public long longValue() {
return (long)get();
} public float floatValue() {
return (float)get();
} public double doubleValue() {
return (double)get();
} }
下面,我们为四种情况(同步关键字、ReentrantLock公平锁和非公平锁、AtomicInteger)做一下性能对比分析,当我们看到上面的代码分析后,我们判断AtomicInteger应该比加锁的方式快,但是实验的结果表明,AtomicInteger只比ReentrantLock加公平锁的情况快几十倍,比其它两种方式略慢一些。
四个demo都用100个线程来循环模拟下单60秒钟:
demo Lesson8SyncIntPerform:在使用同步关键字加锁的情况下100个线程循环下单数为:677337556
demo Lesson8SyncIntPerform:在使用同步关键字加锁的情况下100个线程循环下单数为:755994691
demo Lesson8AtomicIntPerform:在使用AtomicInteger的情况下100个线程循环下单数为:562359607
demo Lesson8AtomicIntPerform:在使用AtomicInteger的情况下100个线程循环下单数为:575367967
demo Lesson8LockIntPerform:在使用ReentrantLock加非公平锁的情况下100个线程循环下单数为:857239882
demo Lesson8LockIntPerform:在使用ReentrantLock加非公平锁的情况下100个线程循环下单数为:860364303
demo Lesson8LockFairIntPerform:在使用ReentrantLock加公平锁的情况下100个线程循环下单数为:19153640
demo Lesson8LockFairIntPerform:在使用ReentrantLock加公平锁的情况下100个线程循环下单数为:19076567
上面的实验结果表明,在jdk1.6及后续的版本中(本实验的jdk版本是1.7,操作系统为windows操作系统),已经对于synchronized关键字的性能优化了很多,已经和ReentrantLock的性能差不多,加锁的效果比不加锁时使用AtomicInteger性能效果还要略好一些,但是公平锁的性能明显降低,其它三种情况下的性能是公平锁性能的几十倍以上,这和公平锁每次试图占有锁时,都必须先要进等待队列,按照FIFO的顺序去获取锁,因此在我们的实验情景下,使用公平锁的线程进行了频繁切换,而频繁切换线程,性能必然会下降的厉害,这也告诫了我们在实际的开发过程中,在需要使用公平锁的情景下,务必要考虑线程的切换频率。
lesson8:AtomicInteger源码解析及性能分析的更多相关文章
- Okhttp3源码解析(3)-Call分析(整体流程)
### 前言 前面我们讲了 [Okhttp的基本用法](https://www.jianshu.com/p/8e404d9c160f) [Okhttp3源码解析(1)-OkHttpClient分析]( ...
- Okhttp3源码解析(2)-Request分析
### 前言 前面我们讲了 [Okhttp的基本用法](https://www.jianshu.com/p/8e404d9c160f) [Okhttp3源码解析(1)-OkHttpClient分析]( ...
- HashMap的源码学习以及性能分析
HashMap的源码学习以及性能分析 一).Map接口的实现类 HashTable.HashMap.LinkedHashMap.TreeMap 二).HashMap和HashTable的区别 1).H ...
- Spring源码解析-AOP简单分析
AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等等,不需要去修改业务相关的代码. 对于这部分内容,同样采用一个简单的例子和源码来说明. 接口 public ...
- Okhttp3源码解析(1)-OkHttpClient分析
### 前言 上篇文章我们讲了[Okhttp的基本用法](https://www.jianshu.com/p/8e404d9c160f),今天根据上节讲到请求流程来分析源码,那么第一步就是实例化OkH ...
- AtomicInteger源码解析
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.原子类 可以实现一些原子操作 基于CAS 下面就以AtomicInteger为例. 2.AtomicIn ...
- 第十一章 AtomicInteger源码解析
1.原子类 可以实现一些原子操作 基于CAS 下面就以AtomicInteger为例. 2.AtomicInteger 在没有AtomicInteger之前,对于一个Integer的线程安全操作,是需 ...
- Jquery源码解析及案例分析
本人刚学先上链接(别人写的挺好的)后期同步补上
- LinkedBlockingQueue源码解析(3)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 4.3.public E take() throws InterruptedException 原理: 将队 ...
随机推荐
- css中的垂直居中方法
单行文字 (外行高度固定) line-height 行高, 将line-height值与外部标签盒子的高度值设置成一致就可以了. height:3em; line-height:3em; 多行文字 图 ...
- 面试题 HashMap 原理
HashMap与HashTable的区别 总结: HashMap是用来代替HashTable的类,一般建议使用HashMap.最核心的区别:HashTable的方法是同步的(线程安全),而HashMa ...
- hdu 1022
// hdu1022 这题算是我做的第一道栈的题目,之前看过栈的一些内容,做这道题的时候,可以模拟出过程,但是具体的代码不会写...所以决定练习一些栈和队列的问题,大概思路就是有三个数组和一个栈,先把 ...
- sqlyog使用注意事项
在sqlyog中执行sql语句时,如果sql语句没有加limit 0,1000; sqlyog会自动查询从0开始的1000条,结果导致mysql慢查系统中显示的sql语句末尾加上了limit 0,10 ...
- jQuery验证框架 .
目录视图 摘要视图 订阅 “程序人生”中国软件开发者职业生涯调查 CSDN社区“三八节”特别活动 开发者职业生涯调查之未来 jQuery验证框架 分类: JQuery 2 ...
- Oracle数据库名、实例名、数据库域名、全局数据库名、服务名之间的区别
数据库名.实例名.数据库域名.全局数据库名.服务名 这是几个令很多初学者容易混淆的概念.相信很多初学者都与我一样被标题上这些个概念搞得一头雾水.我们现在就来把它们弄个明白. 一.数据库名 什么是数据库 ...
- iOS-OC-基础-NSObject常用方法
Person *person1 = [[Person alloc]init]; Person *person2 = [[Person alloc]init]; // 可以调用类中的私有方法,但是会有一 ...
- 消息通信机制NSNotificationCenter -备
消息通信机制NSNotificationCenter的学习.最近写程序需要用到这类,研究了下,现把成果和 NSNotificationCenter是专门供程序中不同类间的消息通信而设置的,使用起来极为 ...
- 汇编下的i++与++i
故事背景,一个正在c语言的家伙,问我++i 和 i++的问题,我当时因为要去上课没给他说,正好今晚有空就测试了一下如下代码: 编译环境:VS2010 语言:C++ #include <iost ...
- Reaver v1.4 用法整理 含高级参数说明 pin必备资料
闲话少叙 使用方法: airmon-ng start wlan0 //启动mon0监控 reaver -i mon0 -b MAC -a -S -vv //普通用法 如果,90.9%进程后死机或停机, ...