多线程原子性问题的产生和解决

原子变量:在 java.util.concurrent.atomic 包下提供了一些原子变量。

1. volatile 保证内存可见性,可以查看atomic中变量是使用volatile来进行修饰的:

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; 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;
}

2. CAS(Compare-And-Swap)比较并交换,算法保证数据变量的原子性
 CAS 算法是硬件对于并发操作的支持
 CAS 包含了三个操作数:
 ①内存值 V
 ②预估值 A
 ③更新值 B
 当且仅当 V == A 时, V = B; 否则,不会执行任何操作。

模拟CAS算法:

/*
* 模拟 CAS 算法
*/
public class TestCompareAndSwap { public static void main(String[] args) {
final CompareAndSwap cas = new CompareAndSwap(); for (int i = 0; i < 10; i++) {
new Thread(new Runnable() { @Override
public void run() {
int expectedValue = cas.get();
boolean b = cas.compareAndSet(expectedValue, (int)(Math.random() * 101));
System.out.println(b);
}
}).start();
} } } class CompareAndSwap{
private int value; //获取内存值
public synchronized int get(){
return value;
} //比较
public synchronized int compareAndSwap(int expectedValue, int newValue){
int oldValue = value; if(oldValue == expectedValue){
this.value = newValue;
} return oldValue;
} //设置
public synchronized boolean compareAndSet(int expectedValue, int newValue){
return expectedValue == compareAndSwap(expectedValue, newValue);
}
}

其他博文关于CAS的详细描述:http://blog.csdn.net/ls5718/article/details/52563959

  1. AtomicBoolean 可以用原子方式更新的 boolean 值。
  2. AtomicInteger 可以用原子方式更新的 int 值。
  3. AtomicIntegerArray 可以用原子方式更新其元素的 int 数组。
  4. AtomicIntegerFieldUpdater<T> 基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。
  5. AtomicLong 可以用原子方式更新的 long 值。
  6. AtomicLongArray 可以用原子方式更新其元素的 long 数组。
  7. AtomicLongFieldUpdater<T> 基于反射的实用工具,可以对指定类的指定 volatile long 字段进行原子更新。
  8. AtomicMarkableReference<V> AtomicMarkableReference 维护带有标记位的对象引用,可以原子方式对其进行更新。
  9. AtomicReference<V> 可以用原子方式更新的对象引用。
  10. AtomicReferenceArray<E> 可以用原子方式更新其元素的对象引用数组。
  11. AtomicReferenceFieldUpdater<T,V> 基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。
  12. AtomicStampedReference<V> AtomicStampedReference 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。

先看下AtomicInteger:

import java.util.concurrent.atomic.AtomicInteger;

/**
* Created by soyoungboy on 2017/3/28.
*/
public class AtomicDemo {
public static void main(String[] args) {
testAtomicInteger();
} private static void testAtomicInteger() {
AtomicInteger atomicInteger = new AtomicInteger(1);
//get()获取当前值。
int x = atomicInteger.get();
System.out.println("get()获取当前值 = " + x);
//addAndGet(int delta)以原子方式将给定值与当前值相加
int i = atomicInteger.addAndGet(2);
System.out.println("addAndGet(int delta)以原子方式将给定值与当前值相加 = " + i);
// decrementAndGet()以原子方式将当前值减 1。
int i1 = atomicInteger.decrementAndGet();
System.out.println("decrementAndGet()以原子方式将当前值减 1 = " + i1);
//doubleValue() 以 double 形式返回指定的数值。
double doubleValue = atomicInteger.doubleValue();
System.out.println("doubleValue() 以 double 形式返回指定的数值。 = " + doubleValue);
//floatValue()以 float 形式返回指定的数值。
float floatValue = atomicInteger.floatValue();
System.out.println("floatValue()以 float 形式返回指定的数值。。 = " + floatValue);
// intValue() 以 int 形式返回指定的数值。
int intValue = atomicInteger.intValue();
System.out.println("intValue() 以 int 形式返回指定的数值。= " + intValue);
//etAndSet(int newValue)以原子方式设置为给定值,并返回旧值。
int andAdd = atomicInteger.getAndAdd(20);
System.out.println("---------------------------------------------------------");
System.out.println("getAndAdd(int delta)以原子方式将给定值与当前值相加。旧值 = " + andAdd);
System.out.println("新值 = " + atomicInteger.get());
System.out.println("---------------------------------------------------------");
//getAndDecrement()以原子方式将当前值加 1。
int andDecrement = atomicInteger.getAndDecrement();
System.out.println("getAndDecrement()以原子方式将当前值减 1。 = " + andDecrement);
//getAndDecrement()以原子方式将当前值减 1。
int andIncrement = atomicInteger.getAndIncrement();
System.out.println("getAndDecrement()以原子方式将当前值减 1。" + andIncrement);
//以原子方式将当前值加 1。
int incrementAndGet = atomicInteger.incrementAndGet();
System.out.println("以原子方式将当前值加 1。" + incrementAndGet); }
}

结果:

get()获取当前值 = 1
addAndGet(int delta)以原子方式将给定值与当前值相加 = 3
decrementAndGet()以原子方式将当前值减 1 = 2
doubleValue() 以 double 形式返回指定的数值。 = 2.0
floatValue()以 float 形式返回指定的数值。。 = 2.0
intValue() 以 int 形式返回指定的数值。= 2
---------------------------------------------------------
getAndAdd(int delta)以原子方式将给定值与当前值相加。旧值 = 2
新值 = 22
---------------------------------------------------------
getAndDecrement()以原子方式将当前值减 1。 = 22
getAndDecrement()以原子方式将当前值减 1。21
以原子方式将当前值加 1。23

原子更新数组

  1. AtomicIntegerArray
  2. AtomicLongArray
  3. AtomicReferenceArray<E>
/**
* Created by soyoungboy on 2017/3/28.
*/
public class AtomicDemo {
public static void main(String[] args) {
testAtomicInteger();
} private static void testAtomicInteger() {
Person person = new Person(11,"小强");
Person person1 = new Person(11,"大强");
Person[] peoples = new Person[]{person,person1};
AtomicReferenceArray<Person> personAtomicReferenceArray = new AtomicReferenceArray<Person>(peoples);
printArray(personAtomicReferenceArray);
// 以原子方式将位置 i 的元素设置为给定值,并返回旧值。
Person person2 = new Person(22, "老秦");
Person andSet = personAtomicReferenceArray.getAndSet(1, person2);
System.out.println("返回的旧值 = "+andSet);
printArray(personAtomicReferenceArray);
//weakCompareAndSet(int i, E expect, E update)
// 如果当前值 == 预期值,则以原子方式将位置 i 的元素设置为给定的更新值。
Person person3 = new Person(23, "哈哈");
//因为上面替换为老秦的person,所以如果是老秦的person,就替换
personAtomicReferenceArray.weakCompareAndSet(1,person2,person3);
printArray(personAtomicReferenceArray);
} private static void printArray(AtomicReferenceArray<Person> personAtomicReferenceArray) {
System.out.println("-------------------------------------");
for (int i = 0;i<personAtomicReferenceArray.length();i++){
String s = personAtomicReferenceArray.get(i).toString();
System.out.println(s);
}
}
}

结果:

-------------------------------------
Person{age=11, name='小强'}
Person{age=11, name='大强'}
返回的旧值 = Person{age=11, name='大强'}
-------------------------------------
Person{age=11, name='小强'}
Person{age=22, name='老秦'}
-------------------------------------
Person{age=11, name='小强'}
Person{age=23, name='哈哈'}

原子更新引用类型

  1. AtomicReference:原子更新引用类型。
  2. AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
  3. AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子更新一个布尔类型的标记位和引用类型。构造方法是AtomicMarkableReference(V initialRef,boolean initialMark)。

举例子:

import java.util.concurrent.atomic.AtomicReference;

/**
* Created by Administrator on 2017/3/28.
*/
public class AtomicReferenceDemo {
public static void main(String[] args) {
testAtomicReference();
} private static void testAtomicReference() {
Person person1 = new Person(1,"姚明");
Person person2 = new Person(2,"易建联");
Person person3 = new Person(3,"王思聪");
AtomicReference<Person> personAtomicReference = new AtomicReference<>(person1);
System.out.println("personAtomicReference"+personAtomicReference.get().toString());
System.out.println("------------------------------------------------------------");
//compareAndSet(V expect, V update)如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
//person2肯定不是期望值,所以不会设置为person3,因此值应该为person1
personAtomicReference.compareAndSet(person2,person3);
System.out.println("personAtomicReference"+personAtomicReference.get().toString());
System.out.println("------------------------------------------------------------");
// getAndSet(V newValue) 以原子方式设置为给定值,并返回旧值。
Person andSet = personAtomicReference.getAndSet(person2);
System.out.println("旧值 = "+andSet);
System.out.println("新值 = "+personAtomicReference.get().toString());
personAtomicReference.lazySet(person3);
//lazySet(V newValue)最终设置为给定值。
System.out.println("lazySet "+personAtomicReference.get().toString());
}
}

结果:

personAtomicReferencePerson{age=1, name='姚明'}
------------------------------------------------------------
personAtomicReferencePerson{age=1, name='姚明'}
------------------------------------------------------------
旧值 = Person{age=1, name='姚明'}
新值 = Person{age=2, name='易建联'}
lazySet Person{age=3, name='王思聪'}

原子更新字段类

  1. AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
  2. AtomicLongFieldUpdater:原子更新长整型字段的更新器。
  3. AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用CAS进行原子更新时可能出现的ABA问题。

newUpdater(Class<U> tclass, String fieldName) 
          使用给定字段为对象创建和返回一个更新器。

这是这个不同于上面类的方法,其他基本上一样。

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
* Created by Administrator on 2017/3/28.
*/
public class AtomicIntegerFieldUpdaterDemo {
public static void main(String[] args) {
testAtomicReference();
} private static void testAtomicReference() {
Person person1 = new Person(1,"姚明");
AtomicReferenceFieldUpdater<Person, String> atomicReferenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(
Person.class, String.class, "name");
// 如果当前值 == 预期值,则以原子方式将此更新器管理的给定对象的字段设置为给定的更新值。
atomicReferenceFieldUpdater.compareAndSet(person1,person1.name,"哈哈");
System.out.println("person1 = "+person1.toString()); AtomicReferenceFieldUpdater<Person, Integer> atomicReferenceFieldUpdater1 = AtomicReferenceFieldUpdater.newUpdater(
Person.class, Integer.class, "age");
// 如果当前值 == 预期值,则以原子方式将此更新器管理的给定对象的字段设置为给定的更新值。
atomicReferenceFieldUpdater1.compareAndSet(person1,person1.age,22);
System.out.println("person1 = "+person1.toString()); }
}

结果:

person1  = Person{age=1, name='哈哈'}
person1 = Person{age=22, name='哈哈'}

java多线程 -- 原子量 变量 CAS的更多相关文章

  1. Java多线程并发06——CAS与AQS

    在进行更近一步的了解Java锁的知识之前,我们需要先了解与锁有关的两个概念 CAS 与 AQS.关注我的公众号「Java面典」了解更多 Java 相关知识点. CAS(Compare And Swap ...

  2. Java多线程中变量的可见性

    之所以写这篇博客, 是因为在csdn上看到一个帖子问的就是这个问题. 废话不多说, 我们先看看他的代码(为了减少代码量, 我将创建线程并启动的部分修改为使用方法引用). 1 2 3 4 5 6 7 8 ...

  3. Java多线程-----原子变量和CAS算法

       原子变量      原子变量保证了该变量的所有操作都是原子的,不会因为多线程的同时访问而导致脏数据的读取问题      Java给我们提供了以下几种原子类型: AtomicInteger和Ato ...

  4. JAVA多线程学习四 - CAS(乐观锁)

    本文讲解CAS机制,主要是因为最近准备面试题,发现这个问题在面试中出现的频率非常的高,因此把自己学习过程中的一些理解记录下来,希望能对大家也有帮助. 什么是悲观锁.乐观锁?在java语言里,总有一些名 ...

  5. Java 多线程 - 原子操作AtomicInteger & CAS(Compare-and-Swap)

    原子类简介:https://www.cnblogs.com/stephen0923/p/4505902.html AtomicInteger 介绍: https://yuwenlin.iteye.co ...

  6. Java多线程详解

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  7. Java多线程并发07——锁在Java中的实现

    上一篇文章中,我们已经介绍过了各种锁,让各位对锁有了一定的了解.接下来将为各位介绍锁在Java中的实现.关注我的公众号「Java面典」了解更多 Java 相关知识点. 在 Java 中主要通过使用sy ...

  8. Java多线程并发08——锁在Java中的应用

    前两篇文章中,为各位带来了,锁的类型及锁在Java中的实现.接下来本文将为各位带来锁在Java中的应用相关知识.关注我的公众号「Java面典」了解更多 Java 相关知识点. 锁在Java中主要应用还 ...

  9. Java多线程系列——原子类的实现(CAS算法)

    1.什么是CAS? CAS:Compare and Swap,即比较再交换. jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronou ...

随机推荐

  1. Python遗传算法工具箱DEAP框架分析

    本文主要介绍python遗传算法工具箱DEAP的实现.先介绍deap的如何使用,再深入介绍deap的框架实现,以及遗传算法的各种实现算法. 代码可以参考 https://github.com/suma ...

  2. Netty源码分析第8章(高性能工具类FastThreadLocal和Recycler)---->第4节: recycler中获取对象

    Netty源码分析第八章: 高性能工具类FastThreadLocal和Recycler 第四节: recycler中获取对象 这一小节剖析如何从对象回收站中获取对象: 我们回顾上一小节demo的ma ...

  3. Next Generation MSP 和传统MSP的区别

    1. 速度 -- 基础架构代码化快速部署 由于物理硬件已经在云上启动并运行,因此配置服务器要快得多.配置完成后,只有当MSP采用DevOps并将环境“基础设施作为代码”时,配置才能快速.云MSP可以轻 ...

  4. [2017 ACL] 对话系统

    Long Papers [Domain adaptation ] 1. Adversarial Adaptation of Synthetic or Stale Data ( Cited by 14 ...

  5. 小刘的深度学习---Faster RCNN

    前言: 对于目标检测Faster RCNN有着广泛的应用,其性能更是远超传统的方法. 正文: R-CNN(第一个成功在目标检测上应用的深度学习的算法) 从名字上可以看出R-CNN是 Faster RC ...

  6. “Hello World!”Final发布文案加美工

    文案: 大家好,我们是“Hello World!”团队,本次我将向大家简要介绍一下空天猎的final发布,在空天猎final发布中,我主要从以下两个方面向大家进行介绍,第一个方面是增加了敌方的boss ...

  7. DataTime日期格式化

    C# DateTime日期格式化 在C#中DateTime是一个包含日期.时间的类型,此类型通过ToString()转换为字符串时,可根据传入给Tostring()的参数转换为多种字符串格式. 目录 ...

  8. “吃神么,买神么”的第一个Sprint计划(第二天)

    “吃神么,买神么”项目Sprint计划 ——5.22(第二天)立会内容与进度 团队组员各自任务: 冯美欣:logo的设计.搜索框的制作,"登陆/注册"的文字链接: 吴舒婷:导航条. ...

  9. 根据C#编程经验思考编程核心

    程序是对数据的各种操作.数据的表示,数据的组织结构,数据的存储,数据的处理,数据的传输等. 程序是由具体的编程语言编写的,不同的编程语言有编写,编译检查,解释执行等过程. 具体的编程语言都有: 1,变 ...

  10. VirtualBox安装及Linux基本操作(操作系统实验一)

    VirtualBox安装教程博客链接(转载)https://blog.csdn.net/u012732259/article/details/70172704 实验名称:Linux的基本操作 实验目的 ...