1、原子类

  • 可以实现一些原子操作
  • 基于CAS

下面就以AtomicInteger为例。

2、AtomicInteger

在没有AtomicInteger之前,对于一个Integer的线程安全操作,是需要使用同步锁来实现的,当然现在也可以通过ReentrantLock来实现,但是最好最方便的实现方式是采用AtomicInteger。

具体示例:

package com.collection.test;

import java.util.concurrent.atomic.AtomicInteger;

/**
* 原子类的测试
*/
public class AtomicTest {
private static AtomicInteger atomicInteger = new AtomicInteger(); //获取当前值
public static void getCurrentValue(){
System.out.println(atomicInteger.get());//-->0
} //设置value值
public static void setValue(){
atomicInteger.set(12);//直接用12覆盖旧值
System.out.println(atomicInteger.get());//-->12
} //根据方法名称getAndSet就知道先get,则最后返回的就是旧值,如果get在后,就是返回新值
public static void getAndSet(){
System.out.println(atomicInteger.getAndSet(15));//-->12
} public static void getAndIncrement(){
System.out.println(atomicInteger.getAndIncrement());//-->15
} public static void getAndDecrement(){
System.out.println(atomicInteger.getAndDecrement());//-->16
} public static void getAndAdd(){
System.out.println(atomicInteger.getAndAdd(10));//-->15
} public static void incrementAndGet(){
System.out.println(atomicInteger.incrementAndGet());//-->26
} public static void decrementAndGet(){
System.out.println(atomicInteger.decrementAndGet());//-->25
} public static void addAndGet(){
System.out.println(atomicInteger.addAndGet(20));//-->45
} public static void main(String[] args) {
AtomicTest test = new AtomicTest();
test.getCurrentValue();
test.setValue();
//返回旧值系列
test.getAndSet();
test.getAndIncrement();
test.getAndDecrement();
test.getAndAdd();
//返回新值系列
test.incrementAndGet();
test.decrementAndGet();
test.addAndGet(); }
}

源代码:

    private volatile int value;// 初始化值

    /**
* 创建一个AtomicInteger,初始值value为initialValue
*/
public AtomicInteger(int initialValue) {
value = initialValue;
} /**
* 创建一个AtomicInteger,初始值value为0
*/
public AtomicInteger() {
} /**
* 返回value
*/
public final int get() {
return value;
} /**
* 为value设值(基于value),而其他操作是基于旧值<--get()
*/
public final void set(int newValue) {
value = newValue;
} public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
} /**
* 基于CAS为旧值设定新值,采用无限循环,直到设置成功为止
*
* @return 返回旧值
*/
public final int getAndSet(int newValue) {
for (;;) {
int current = get();// 获取当前值(旧值)
if (compareAndSet(current, newValue))// CAS新值替代旧值
return current;// 返回旧值
}
} /**
* 当前值+1,采用无限循环,直到+1成功为止
* @return the previous value 返回旧值
*/
public final int getAndIncrement() {
for (;;) {
int current = get();//获取当前值
int next = current + 1;//当前值+1
if (compareAndSet(current, next))//基于CAS赋值
return current;
}
} /**
* 当前值-1,采用无限循环,直到-1成功为止
* @return the previous value 返回旧值
*/
public final int getAndDecrement() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return current;
}
} /**
* 当前值+delta,采用无限循环,直到+delta成功为止
* @return the previous value 返回旧值
*/
public final int getAndAdd(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return current;
}
} /**
* 当前值+1, 采用无限循环,直到+1成功为止
* @return the updated value 返回新值
*/
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;//返回新值
}
} /**
* 当前值-1, 采用无限循环,直到-1成功为止
* @return the updated value 返回新值
*/
public final int decrementAndGet() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return next;//返回新值
}
} /**
* 当前值+delta,采用无限循环,直到+delta成功为止
* @return the updated value 返回新值
*/
public final int addAndGet(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return next;//返回新值
}
} /**
* 获取当前值
*/
public int intValue() {
return get();
}

说明:使用与源代码都简单到爆了!自己看看注释。

注意:

  • value是volatile的,关于volatile的相关内容见《附2 volatile》,具体链接:http://www.cnblogs.com/java-zhao/p/5125698.html
  • 单步操作:例如set()是直接对value进行操作的,不需要CAS,因为单步操作就是原子操作。
  • 多步操作:例如getAndSet(int newValue)是两步操作-->先获取值,在设置值,所以需要原子化,这里采用CAS实现。
  • 对于方法是返回旧值还是新值,直接看方法是以get开头(返回旧值)还是get结尾(返回新值)就好
  • CAS:比较CPU内存上的值是不是当前值current,如果是就换成新值update,如果不是,说明获取值之后到设置值之前,该值已经被别人先一步设置过了,此时如果自己再设置值的话,需要在别人修改后的值的基础上去操作,否则就会覆盖别人的修改,所以这个时候会直接返回false,再进行无限循环,重新获取当前值,然后再基于CAS进行加减操作。
  • 如果还是不懂CAS,类比数据库的乐观锁

补充一个东西:

     // 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"));
} catch (Exception ex) { throw new Error(ex); }
} private volatile int value;

这是AtomicInteger的所有属性,其中value存的是当前值,而当前值存放的内存地址可以通过valueOffset来确定。实际上是“value字段相对Java对象的起始地址的偏移量”

     public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

CAS方法:通过对比“valueOffset上的value”与expect是否相同,来决定是否修改value值为update值。

ABA 问题:执行流程如下

int i=100;

Thread1: 100->101->100

Thread2:                 100-> 200

Thread2没有发现Thread1的中间i发生了变化的过程。

ABA问题怎么解决?

第一种姿势:不处理,对于只关注最后的结果,不关注中间变化过程,ABA 无所谓,例如 AtomicInteger.compareAndset(...)

第二种姿势:使用版本号控制,eg. AtomicStampedReference 提供一个版本号,过程如下

Thread1: 100 1->101 2->100 3(前边为数据,空格后是版本号)

Thread2:                      100 1-> 200(此时报错,版本号不一致)

那版本号是否也会出现ABA问题,只要后续的版本号不会出现之前的版本号就行(eg. 递增)

第十一章 AtomicInteger源码解析的更多相关文章

  1. 第九章 LinkedBlockingQueue源码解析

    1.对于LinkedBlockingQueue需要掌握以下几点 创建 入队(添加元素) 出队(删除元素) 2.创建 Node节点内部类与LinkedBlockingQueue的一些属性 static ...

  2. 第六章 ReentrantLock源码解析2--释放锁unlock()

    最常用的方式: int a = 12; //注意:通常情况下,这个会设置成一个类变量,比如说Segement中的段锁与copyOnWriteArrayList中的全局锁 final Reentrant ...

  3. 第十四章 Executors源码解析

    前边两章介绍了基础线程池ThreadPoolExecutor的使用方式.工作机理.参数详细介绍以及核心源码解析. 具体的介绍请参照: 第十二章 ThreadPoolExecutor使用与工作机理 第十 ...

  4. 第零章 dubbo源码解析目录

    第一章 第一个dubbo项目 第二章  dubbo内核之spi源码解析 2.1  jdk-spi的实现原理 2.2 dubbo-spi源码解析 第三章 dubbo内核之ioc源码解析 第四章 dubb ...

  5. 第十三章 ThreadPoolExecutor源码解析

    ThreadPoolExecutor使用方式.工作机理以及参数的详细介绍,请参照<第十二章 ThreadPoolExecutor使用与工作机理 > 1.源代码主要掌握两个部分 线程池的创建 ...

  6. 第四章 CopyOnWriteArraySet源码解析

    注:在看这篇文章之前,如果对CopyOnWriteArrayList底层不清楚的话,建议先去看看CopyOnWriteArrayList源码解析. http://www.cnblogs.com/jav ...

  7. 第二章 ConcurrentHashMap源码解析

    注:在看这篇文章之前,如果对HashMap的层不清楚的话,建议先去看看HashMap源码解析. http://www.cnblogs.com/java-zhao/p/5106189.html 1.对于 ...

  8. 第三章 CopyOnWriteArrayList源码解析

    注:在看这篇文章之前,如果对ArrayList底层不清楚的话,建议先去看看ArrayList源码解析. http://www.cnblogs.com/java-zhao/p/5102342.html ...

  9. 第六章 HashSet源码解析

    6.1.对于HashSet需要掌握以下几点 HashSet的创建:HashSet() 往HashSet中添加单个对象:即add(E)方法 删除HashSet中的对象:即remove(Object ke ...

随机推荐

  1. Windows10怎么架设局域网DNS服务器?

    已采纳 需要安装Windows组件进行设置.最好是安装服务器版本的Windows. 1. 安装DNS服务 开始—〉设置—〉控制面板—〉添加/删除程序—〉添加/删除Windows组件—〉“网络服务”—〉 ...

  2. 洛谷P2525 Uim的情人节礼物·其之壱 [康托展开]

    题目传送门 Uim的情人节礼物·其之壱 题目描述 情人节到了,Uim打算给他的后宫们准备情人节礼物.UIm一共有N(1<=N<=9)个后宫妹子(现充去死 挫骨扬灰!). 为了维护他的后宫的 ...

  3. Qt Quick快速入门之qml布局

    Qml里面布局主要有两种,锚点布局.Grid布局. 锚点布局使用anchors附件属性将一个元素的边定位到另一个元素的边,从而确定元素的位置和大小.下面是示例 import QtQuick 2.3 i ...

  4. [BZOJ5305][HAOI2018]苹果树(DP)

    首先注意到每种树都是等概率出现的,于是将问题转化成计数求和问题. f[n]表示所有n个点的树的两两点距离和的总和. g[n]表示所有n个点的树的所有点到根的距离和的总和. h[n]表示n个点的树的可能 ...

  5. python开发_tkinter_修改tkinter窗口的红色图标'Tk'

    学过java的swing可能知道,在创建一个窗口的时候,窗口的左上角是一个咖啡图标 如下图所示: 在python中,tkinter模块生成的窗口左上角是一个:Tk字样的图标(Tk为tkinter的缩写 ...

  6. h5的部分新元素或属性

    #h5的新元素或属性 - datalist标签(新表单元素):规定了input元素可能的选项列表.   * 条件:datalist的id属性值必须等于input元素的list属性值.          ...

  7. MySQL从库com_insert无变化的原因

    大家都知道com_insert等com_xxx参数可以用来监控数据库实例的访问量,也就是我们常说的QPS.并且基于MySQL的复制原理,所有主库执行的操作都会在从库重放一遍保证数据一致,那么主库的co ...

  8. android点滴之ContentObserver的使用

    一概念 ContentObserver用于观察(捕捉)特定Uri引起的数据的变化,继而做一些对应的处理,当ContentObserver所观察的Uri发生变化时,便会触发它. 从概念看ContentO ...

  9. ios项目开发(天气预报项目):使用正则获取 weather.com.cn站点信息

    NSString *pattern = @"(?<=<td class=\"bigblod\">).*?(?=</td>)"; 2 ...

  10. 关于Google Android平台的ClockworkMod Recovery恢复模式

    lockworkMod Recovery,它也被称为Clockwork与CWM,它是装载Google Android操作系统设备的一个自定义的Recovery恢复模式,它可以使得相关Android设备 ...