java中,可能有一些场景,操作非常简单,但是容易存在并发问题,比如i++,

此时,如果依赖锁机制,可能带来性能损耗等问题,

于是,如何更加简单的实现原子性操作,就成为java中需要面对的一个问题。

在backport-util-concurrent没有被引入java1.5并成为JUC之前,

这些原子类和原子操作方法,都是使用synchronized实现的。

不过JUC出现之后,这些原子操作 基于JNI提供了新的实现,

比如AtomicInteger,AtomicLong,AtomicBoolean,AtomicReference,AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray;

这些操作中提供一些原子化操作,比如incrementAndGet(相当于i++),compareAndSet(安全赋值)等,直接读源代码也很容易懂。

以AtomicInteger为例,看看它是怎么做到的:

如果是读取值,很简单,将value声明为volatile的,就可以保证在没有锁的情况下,数据是线程可见的:

1     private volatile int value;

      public final int get() {
2 return value;
3 }

那么,涉及到值变更的操作呢?以AtomicInteger实现:++i为例:

1     public final int incrementAndGet() {
2 for (;;) {
3 int current = get();
4 int next = current + 1;
5 if (compareAndSet(current, next))
6 return next;
7 }
8 }

在这里采用了CAS操作,每次从内存中读取数据然后将此数据和+1后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。

而这里的comparAndSet(current,next),就是前面介绍CAS的时候所说的依赖JNI实现的乐观锁做法:

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

数组原子化

注意,Java中Atomic*Array,并不是对整个数组对象实现原子化(也没有必要这样做),而是对数组中的某个元素实现原子化。

例如,对于一个整型原子数组,其中的原子方法,都是对每个元素的:

1 public final int getAndDecrement(int i) {
2 while (true) {
3 int current = get(i);
4 int next = current - 1;
5 if (compareAndSet(i, current, next))
6 return current;
7 }
8 }

引用的原子化操作

引用的操作本身不就是原子的吗?

一个对象的引用,从A切换到B,本身也不会出现 非原子操作啊?这种想法本身没有什么问题,

但是考虑下嘛的场景:对象a,当前执行引用a1,

线程X期望将a的引用设置为a2,也就是a=a2,

线程Y期望将a的引用设置为a3,也就是a=a3。

X要求,a必须从a1变为a2,也就是说compareAndSet(expect=a1,setValue=a2);

Y要求,a必须从a1变为a3,也就是说compareAndSet(expect=a1,setValue=a3)。

如果严格遵循要求,应该出现X把a的引用设置为a2后,Y线程操作失败的情况,也就是说:

X:a==a1--> a=a2;

Y:a!=a1 --> Exception;

如果没有原子化,那么Y会直接将a赋值为a3,从而导致出现脏数据。

这就是原子引用AtomicReference存在的原因。

1      public final V getAndSet(V newValue) {
2 while (true) {
3 V x = get();
4 if (compareAndSet(x, newValue))
5 return x;
6 }
7 }

注意,AtomicReference要求引用也是volatile的。

Updater原子化

其它几个Atomic类,都是对被volatile修饰的基本数据类型的自身数据进行原子化操作,

但是如果一个被volatile修饰的变量本身已经存在在类中,那要如何提供原子化操作呢?

比如,一个Person,其中有个属性为age,private volatile int age,

如何对age提供原子化操作呢?

1 private AtomicIntegerFieldUpdater<Person> updater = AtomicIntegerFieldUpdater.newUpdater(Person.class, "age");
2 updater.getAndIncrement(5);//加5岁
3 updater.compareAndSet(person, 30, 35)//如果一个人的年龄是30,设置为35。

Java中的Atomic包使用指南

参考: http://ifeve.com/java-atomic/

深入理解java:2.3.1. 并发编程concurrent包 之Atomic原子操作(循环CAS)的更多相关文章

  1. Python并发编程-concurrent包

    Python并发编程-concurrent包 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.concurrent.futures包概述 3.2版本引入的模块. 异步并行任务编程 ...

  2. 深入理解java:2.3.4. 并发编程concurrent包 之容器ConcurrentLinkedQueue(非阻塞的并发队列---循环CAS)

    1.    引言 在并发编程中我们有时候需要使用线程安全的队列. 如果我们要实现一个线程安全的队列有两种实现方式:一种是使用阻塞算法,另一种是使用非阻塞算法. 使用阻塞算法的队列可以用一个锁(入队和出 ...

  3. 深入理解java:2.3.2. 并发编程concurrent包 之重入锁/读写锁/条件锁

    重入锁 Java中的重入锁(即ReentrantLock)   与JVM内置锁(即synchronized)一样,是一种排它锁. ReentrantLock提供了多样化的同步,比如有时间限制的同步(定 ...

  4. 深入理解java:2.3.3. 并发编程concurrent包 之容器ConcurrentHashMap

    线程不安全的HashMap 因为多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap. 效率低下的HashTable容器 H ...

  5. 深入理解java:2.3. 并发编程 java.util.concurrent包

    JUC java.util.concurrent包, 这个包是从JDK1.5开始引入的,在此之前,这个包独立存在着,它是由Doug Lea开发的,名字叫backport-util-concurrent ...

  6. 深入理解java:2.3.6. 并发编程concurrent包 之管理类---线程池

    我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁 ...

  7. 深入理解java:2.3.5. 并发编程concurrent包 之容器BlockingQueue(阻塞队列)

    1. 什么是阻塞队列? 阻塞队列(BlockingQueue)是一个支持两个附加操作的队列. 这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空. 当队列满时,存储元素的线程会等待队列 ...

  8. boost并发编程boost::atomic

    三个用于并发编程的组件: atomic,thread,asio(用于同步和异步io操作)   atomic atomic,封装了不同计算机硬件的底层操作原语,提供了跨平台的原子操作功能,解决并发竞争读 ...

  9. Java 面试宝典!并发编程 71 道题及答案全送上!

    金九银十跳槽季已经开始,作为 Java 开发者你开始刷面试题了吗?别急,我整理了71道并发相关的面试题,看这一文就够了! 1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程( ...

随机推荐

  1. ubuntu 设置apt-get 代理

    1 添加apt-get 代理配置文件 sudo vi /etc/apt/apt.conf.d/proxy.conf 2 添加内容 Acquire::http::Proxy "http://w ...

  2. MyEclipse使用教程:unattended安装

    [MyEclipse CI 2019.4.0安装包下载] 以下内容适用于2013及以上版本. 运行无提示安装程序 1. 创建一个unattended response文件. 2. 要激活unatten ...

  3. 界面开发工具集Telerik UI for ASP.NET AJAX发布R2 2019

    Telerik UI for ASP.NET AJAX拥有构建ASP.NET AJAX和SharePoint应用程序的80+控件,将为任何浏览器和设备构建Web Forms应用程序的时间缩短一半.Te ...

  4. 别再误解MySQL和「幻读」了

    The so-called phantom problem occurs within a transaction when the same query produces different set ...

  5. java的一些总结

    抽象方法和普通方法的区别???? 1.抽象方法必须要通过继承才能被实现,然后才能被对象调用:普通方法在定义的同时就已经实现了. 2.抽象方法只需声明,而不需实现某些功能 3.抽象方法必须要被重写 20 ...

  6. sqlserver表值函数调用方式

    Connection conn = sqlServerManage.sqlServerConn(); Statement stmt; ResultSet rs; // 组装sql StringBuff ...

  7. Sublime text3配置C/C++编译环境

    安装sublime text3后,一直很喜欢使用它看代码(这个高亮配色真的很好看).它默认的运行环境就有C/C++,在写了一个hello world!后正常输出,但在加入scanf()输入后就不行了. ...

  8. D. Restore Permutation

    D. Restore Permutation 就是给一个n个数的全排,然后bi记录比ai小且在排在ai前面的数的和,求ai 树状数组维护,二分 #include<bits/stdc++.h> ...

  9. 超实用的PHP代码片段!

    摘要:本文分享了九个超级有用的PHP代码片段,当你在开发网站.应用或者博客时,利用这些代码能为你节省大量的时间.你可以直接拿来用! 此前,研发频道曾发布<直接拿来用,10个PHP代码片段> ...

  10. Python实现用户注册到文件

    import getpass #引入getpass模块,主要用来输入密码关闭回显 f=open('d:/user','a+') #定义文件对象并以追加方式打开,不存在就创建 f.seek(0) #文件 ...