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,类比数据库的乐观锁

补充一个东西:

 1     // setup to use Unsafe.compareAndSwapInt for updates
2 private static final Unsafe unsafe = Unsafe.getUnsafe();
3 private static final long valueOffset;
4
5 static {
6 try {
7 valueOffset = unsafe.objectFieldOffset
8 (AtomicInteger.class.getDeclaredField("value"));
9 } catch (Exception ex) { throw new Error(ex); }
10 }
11
12 private volatile int value;

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

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

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. 第八章 ArrayBlockingQueue源码解析

    注意:在阅读本文之前或在阅读的过程中,需要用到ReentrantLock,内容见<第五章 ReentrantLock源码解析1--获得非公平锁与公平锁lock()><第六章 Reen ...

  2. lesson8:AtomicInteger源码解析及性能分析

    AtomicInteger等对象出现的目的主要是为了解决在多线程环境下变量计数的问题,例如常用的i++,i--操作,它们不是线程安全的,AtomicInteger引入后,就不必在进行i++和i--操作 ...

  3. AtomicInteger源码解析

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.原子类 可以实现一些原子操作 基于CAS 下面就以AtomicInteger为例. 2.AtomicIn ...

  4. 第十一章 AtomicInteger源码解析

    1.原子类 可以实现一些原子操作 基于CAS 下面就以AtomicInteger为例. 2.AtomicInteger 在没有AtomicInteger之前,对于一个Integer的线程安全操作,是需 ...

  5. LinkedBlockingQueue源码解析(3)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 4.3.public E take() throws InterruptedException 原理: 将队 ...

  6. 第九章 LinkedBlockingQueue源码解析

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

  7. 多线程爬坑之路-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析

    Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作. 一.Atomic包下的所有类如下表: 类摘要 AtomicBoolean 可以用原子方式更新的 ...

  8. [Java多线程]-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析

    Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作. 一.Atomic包下的所有类如下表: 类摘要 AtomicBoolean 可以用原子方式更新的 ...

  9. Android -- AsyncTask源码解析

    1,前段时间换工作的时候,关于AsyncTask源码这个点基本上大一点的公司都会问,所以今天就和大家一起来总结总结.本来早就想写这篇文章的,当时写<Android -- 从源码解析Handle+ ...

  10. ThreadPoolExecutor系列<三、ThreadPoolExecutor 源码解析>

    本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7681826.html 在源码解析前,需要先理清线程池控制的运行状态 ...

随机推荐

  1. CSP-S 2024 简单题

    CSP-S 2024 简单题 以下均为考场做法. T1 决斗 (duel) 考虑贪心,按照攻击力 \(a_i\) 排序,从小到大使用所有怪物进行攻击,每只怪物攻击一个在场且能击杀的怪物中,攻击力最大的 ...

  2. 一文彻底搞定Spring Security 认证,实现登陆登出功能

    Spring Security 是一个强大且灵活的安全框架,提供了身份验证(认证)和授权(授权)功能.下面我们将详细介绍 Spring Security 的认证功能流程,并提供自定义实现登录接口的示例 ...

  3. 多进程可以共享内存,那么多进程是否可以共享显存呢?(CPU->内存,GPU->显存)

    多进程可以共享内存,那么多进程是否可以共享显存呢?(CPU->内存,GPU->显存) 答案:不能.多进程可以共享内存,但是多进程不能共享显存(NVIDIA GPU 显存不能被多进程共享). ...

  4. 2-2 C++变量

    目录 2.2.1 变量定义:列表初始化(list initialization) 2.2.2 变量的定义与声明 C++分离式编译 定义与声明 2.2.3 C++变量命名 2.2.4 变量名的作用域(s ...

  5. position 的值, relative 和 absolute 分别是相对于谁进行定位的?

    relative:  相对定位 相对于自己本身在正常文档流中的位置进行定位 相对它原来的位置.原来在标准流中的位置继续占有. absolute: 绝对定位 相对于最近一级定位不为static的父元素进 ...

  6. [TAD] Triangles of Absolute Differences-反帕斯卡三角形

    [IMO2018] Triangles of Absolute Differences-反帕斯卡三角形 前言 叠甲 笔者不是学数竞的,在此感谢我的数竞生为我讲解题目. 笔者学艺不精,且知识面浅薄. 所 ...

  7. OpenWRT/iStoreOS 不从头编译内核安装4G LTE网卡 Quectel EM05-CE记录

    我的机器是x86装了iStoreOS,有4G网卡Quectel EM05 https://www.quectel.com/cn/product/lte-em05 主要参考资料如下 https://ww ...

  8. 从Hbase shell理解列式存储

    列存储和行存储在理解上的差别挺大,特别是在非常数据行存储之后. 在行存储中,每张表的结构是固定的,某一列可以没有值但是这一列是必须在的.那么可以理解行存储的数据是结构化的. 但是列存储确有每行的数据却 ...

  9. .NET周刊【12月第1期 2024-12-01】

    我在.NET Conf China 2024 等你! .NET Conf China 2024 是一场面向开发人员的社区盛会,旨在庆祝 .NET 9 的发布,并回顾过去一年 .NET 在中国的发展成就 ...

  10. Java基础 —— 集合(二)

    Collection 接口 Collection接口常用方法 boolean add(E e):在集合末尾添加元素 boolean remove(Object o):若集合中存在与o相同的元素,则删除 ...