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

原子变量:在 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. ubuntu HackRF One相关环境搭建

    本文内容.开发板及配件仅限用于学校或科研院所开展科研实验! 淘宝店铺名称:开源SDR实验室 HackRF链接:https://item.taobao.com/item.htm?spm=a1z10.1- ...

  2. 吴恩达(Andrew Ng)——机器学习笔记1

    之前经学长推荐,开始在B站上看Andrew Ng的机器学习课程.其实已经看了1/3了吧,今天把学习笔记补上吧. 吴恩达老师的Machine learning课程共有113节(B站上的版本https:/ ...

  3. git ssh密钥配置添加

    1.  初次安装git配置用户名和邮箱 $ git config --global user.name "xxx" $ git config --global user.email ...

  4. mybatis学习------打包xml映射文件

    编译mybatis时,idea不会将mybatis的xml映射文件一起打包进jar,即在编译好的jar包里缺少mybatis映射文件,导致网站加载失败 为解决这个问题,可在mybatis对应modul ...

  5. [T-ARA][느낌 아니까][懂得那份感觉]

    歌词来源:http://music.163.com/#/song?id=27808771 作曲 : 박덕상/박현중 [作曲 : p/bag-ddeog-ssang-/p/ba-Kyeon-c/jung ...

  6. dos2unix命令详解

    基础命令学习目录首页 原文链接:https://blog.csdn.net/leedaning/article/details/53024290 使用git 的时候碰到git将unix换行符转换为wi ...

  7. Linux 基础入门第一次实验笔记

    第一节.实验介绍 本节主要介绍 Linux 的历史,Linux 与 Windows 的区别等入门知识.如果你已经有过充分的了解,可以跳过本节,直接进入下一个实验. 一.Linux 为何物 Linux ...

  8. <!CDATA[]]用法详解

    所有 XML 文档中的文本均会被解析器解析. 只有 CDATA 区段(CDATA section)中的文本会被解析器忽略. PCDATA PCDATA 指的是被解析的字符数据(Parsed Chara ...

  9. php之 常用的 流程管理

    1.流程管理的用法是什么样的? 2.怎么发起想要的流程? 3.审批的人要是怎么审批通过? 4.流程审核是不是要挨个走过? 一.要有数据库的内容的 肯定会有表的,首先就是用户表了,然后就是流程表,用户编 ...

  10. 树莓派配置RTC时钟(DS3231,I2C接口)

    1.购买基于DS3231的RTC时钟模块,并且支持3.3V的那种 2.配置树莓派 a.打开树莓派的i2c接口 sudo raspi-config -->Interfacing Options - ...