1 AtomicInteger解析
众所周知,在多线程并发的情况下,对于成员变量,可能是线程不安全的;
一个很简单的例子,假设我存在两个线程,让一个整数自增1000次,那么最终的值应该是1000;但是多线程情况下并不能保证原子性;最终的结果极有可能不是1000;看如下的代码:
package automic;

public class AtomicIntegerTest extends Thread{

    private Integer count=0;
@Override
public void run() {
for(int i=1;i<=500;i++){
count++;
}
System.out.println("count的值是:"+ count); } public static void main(String[] args) {
AtomicIntegerTest a=new AtomicIntegerTest();
Thread t1 = new Thread(a);
Thread t2 = new Thread(a);
t1.start();
t2.start(); } }
最终的结果无论如何都是小于1000的,因为两个线程可能同时去修改了变量的值导致的;
使用AtomicInteger保证线程安全。AtomicInteger类,是一个对于变量可以进行原子性操作的类;核心是CAS无锁算法。
CAS算法是基于乐观锁的实现方法,在不需要锁的情况下,并且在并发量不高的情况下完成的原子性的操作。主要原理是当一个线程去修改这个值的时候,会进入一个while循环,并且不断的尝试comparreAndSet()方法,也就是不断地比较内存值和期望值,如果相等就修改,不相等就返回false,当值修改完毕结束死循环;
对于上述的代码,我们可以将Integer修改为AutomicInteger,且看如下的代码:
import java.util.concurrent.atomic.AtomicInteger;

public
class AtomicIntegerTest2 extends Thread{
/**
* 这里使用了AtomicInteger类,这是一个对于变量可以进行原子性操作的类;核心是CAS无锁算法;
* 下面两个构造器其中一个进行了值得初始化
* public AtomicInteger(int initialValue) {
* value = initialValue;
* }
* public AtomicInteger() {
* }
*/ private AtomicInteger count=new AtomicInteger(0);
@Override
public void run() {
for(int i=1;i<=500;i++){
/**
* getAndIncrement是以原子的方式给当前值加1
*/
count.getAndIncrement();
}
System.out.println("count的值是:"+ count); } public static void main(String[] args) {
AtomicIntegerTest2 a=new AtomicIntegerTest2();
Thread t1 = new Thread(a);
Thread t2 = new Thread(a);
t1.start();
t2.start(); } }
最终的结果是1000;
采用AtomicInteger可以保证数据的原子性操作,多线程并发的情况下是安全的;
对于线程的安全来说,是一个老生常谈的问题:做到线程安全,我们最直接的方法一般有两种:
    1> 同步锁或者同步代码块
    2> 互斥锁或者重入锁
其实上述的两种都是利用锁的方式来解决线程并发问题,而且都是悲观锁的方式;
synchrnoized在jdk1.6之后做了优化,在性能上和Lock锁处于相同的数量级的位置上;
synchnoized本身就具备了原子性操作;即锁的方式本身就保证了数据操作的原子性;
而CAS算法没有利用锁的技术;他如何实现的呢?
且看如下的源代码:
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;
}
① Unsafe是CAS的核心类,一切底层的具体实现由他来完成;
② valueOffset 变量在内存中地址的起始偏移量;
如下的静态代码块是完成变量的初始化;当JVM加载该类的时候就为这个变量在内存中开辟内存地址;它通过反射的手法获取字段value的值,而value的值使用了volatile去修饰,保证了内存的可见性(这点至关重要);但是volatile本身不可以保证操作的原子性;
 static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
 
再来看一看这个方法getAndIncrement();表示给特定的变量添加1;这个方法的源码如下:为了明晰原理,我这里使用的是jdk1.7
 public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}

在看看jdk1.8的源码:

public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}

使用了unsafe的方法,其实二者底层的实现方式都差不多:

进入一个for循环,不断的比较内存值和期望值,如果相等就修改,不相等就返回false;

    /**
* set()方法 ,设置一个值
*/
public final void set(int newValue) {
value = newValue;
} /**
* lazySet()方法,没有storeload屏障的set,出现于JDK1.6
*/
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
} /**
* getAndSet()方法 原子性的获取并且设置值
*/
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
} /**
* 如果当前值和内存值相等,那么进行更新
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
} /**
* weak的CAS,也就是没有volatile语义的CAS,没有加入内存屏障
*/
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
} /**
* 自增加,返回原来的值.
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
} /**
* 自减少,返回原来的值
*/
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -1);
} /**
* 原子性的增加delta的值
*/
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
} /**
* 自增1
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
} /**
* 自减1
*/
public final int decrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
} /**
* 阻塞式更新,并且对prev进行一个IntUnaryOperator操作运算
*/
public final int updateAndGet(IntUnaryOperator updateFunction) {
int prev, next;
do {
prev = get();
next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next));
return next;
} /**
* 阻塞式更新,并对prev和x,进行二元运算操作。于jdk1.8出现
*/
public final int getAndAccumulate(int x,
IntBinaryOperator accumulatorFunction) {
int prev, next;
do {
prev = get();
next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSet(prev, next));
return prev;
}
 
 
原文地址:

java并发:AtomicInteger 以及CAS无锁算法【转载】的更多相关文章

  1. CAS无锁算法与ConcurrentLinkedQueue

    CAS:Compare and Swap 比较并交换 java.util.concurrent包完全建立在CAS之上的,没有CAS就没有并发包.并发包借助了CAS无锁算法实现了区别于synchroni ...

  2. Java并发基础:了解无锁CAS就从源码分析

    https://segmentfault.com/a/1190000015881923

  3. 【Java并发编程】9、非阻塞同步算法与CAS(Compare and Swap)无锁算法

    转自:http://www.cnblogs.com/Mainz/p/3546347.html?utm_source=tuicool&utm_medium=referral 锁(lock)的代价 ...

  4. 非阻塞同步算法与CAS(Compare and Swap)无锁算法

    锁(lock)的代价 锁是用来做并发最简单的方式,当然其代价也是最高的.内核态的锁的时候需要操作系统进行一次上下文切换,加锁.释放锁会导致比较多的上下文切换和调度延时,等待锁的线程会被挂起直至锁释放. ...

  5. (转载)java高并发:CAS无锁原理及广泛应用

    java高并发:CAS无锁原理及广泛应用   版权声明:本文为博主原创文章,未经博主允许不得转载,转载请注明出处. 博主博客地址是 http://blog.csdn.net/liubenlong007 ...

  6. 【实战Java高并发程序设计6】挑战无锁算法:无锁的Vector实现

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...

  7. 无锁算法CAS 概述

    无锁算法CAS 概述 JDK5.0以后的版本都引入了高级并发特性,大多数的特性在java.util.concurrent包中,是专门用于多线并发编程的,充分利用了现代多处理器和多核心系统的功能以编写大 ...

  8. 具体CAS操作实现(无锁算法)

    具体CAS操作 上一篇讲述了CAS机制,这篇讲解CAS具体操作. 什么是悲观锁.乐观锁?在java语言里,总有一些名词看语义跟本不明白是啥玩意儿,也就总有部分面试官拿着这样的词来忽悠面试者,以此来找优 ...

  9. CAS(Compare and Swap)无锁算法-学习笔记

    非阻塞同步算法与CAS(Compare and Swap)无锁算法 这篇问题对java的CAS讲的非常透彻! 锁的代价 1. 内核态的锁的时候需要操作系统进行一次上下文切换,加锁.释放锁会导致比较多的 ...

随机推荐

  1. 【Spring学习笔记-MVC-4】SpringMVC返回Json数据-方式2

    <Spring学习笔记-MVC>系列文章,讲解返回json数据的文章共有3篇,分别为: [Spring学习笔记-MVC-3]SpringMVC返回Json数据-方式1:http://www ...

  2. bzoj2464 小明的游戏

    Description 小明最近喜欢玩一个游戏.给定一个n * m的棋盘,上面有两种格子#和@.游戏的规则很简单:给定一个起始位置和一个目标位置,小明每一步能向上,下,左,右四个方向移动一格.如果移动 ...

  3. OSPF里几个特殊区域(stub、Totally stubby、NSSA、Totally NSSA)总结

    网友总结: 简单的说,就是 stub过滤4,5类lsa,ABR会产生缺省的3类lsa,区域内不能引入外部路由 total stub过滤3,4,5类lsa,ABR会产生缺省的3类lsa,区域内不能引入外 ...

  4. 搭建pyspider爬虫服务

    1. 环境准备 首先yum更新 yum update -y 安装开发编译工具 yum install gcc gcc-c++ -y 安装依赖库 yum install python-pip pytho ...

  5. Jenkins 邮件发送设置(jenkins自带邮件设置)

    首先进入系统设置,找到Jenkins Location部分 这里设置 系统管理员邮件地址,然后设置邮件通知部分,这里为了方便我使用了QQ邮箱(作为发送邮件地址) 这里的 用户名 必须与上面的 系统管理 ...

  6. 文件查找find命令

    find命令总结: 常用选项: -name 查找/etc目录下以conf结尾的文件 find /etc -name '*conf' -iname 查找当前目录下文件名为aa的文件,不区分大小写 fin ...

  7. bootStrap 教程 文档

    参考1: https://www.w3schools.com/bootstrap/default.asp 参考1:http://www.runoob.com/bootstrap/bootstrap-i ...

  8. 使用Apache POI处理excel公式不更新的解决办法

    使用poi更新excel时,如果单元格A设置了公式,当其依赖的其他单元格填充了值之后,导出的excel中A仍为公式而不是自动计算的值,如图: Paste_Image.png 分值小计没有更新成计算结果 ...

  9. Android 照相

    XE6 控件太强了CameraComponent就可以了 CameraComponent1.Active := True; procedure TCameraComponentForm.CameraC ...

  10. 机器学习入门-DBSCAN聚类算法

    DBSCAN 聚类算法又称为密度聚类,是一种不断发张下线而不断扩张的算法,主要的参数是半径r和k值 DBSCAN的几个概念: 核心对象:某个点的密度达到算法设定的阈值则其为核心点,核心点的意思就是一个 ...