资讯专栏INFORMATION COLUMN

【Java并发编程的艺术】第二章读书笔记之原子操作

olle / 3457人阅读

摘要:前言今天的笔记来了解一下原子操作以及中如何实现原子操作。概念原子本意是不能被进一步分割的最小粒子,而原子操作意为不可被中断的一个或一系列操作。处理器实现原子操作处理器会保证基本内存操作的原子性。

前言

今天的笔记来了解一下原子操作以及Java中如何实现原子操作。

概念

原子(atomic)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为“不可被中断的一个或一系列操作”。

处理器实现原子操作

处理器会保证基本内存操作的原子性。处理器保证从系统内存中读取或者写入一个字节是原子的,意思是当一个处理器读取一个字节时,其他处理器不能访问这个字节的内存地址。最新的处理器能自动保证单处理器进行16/32/64位的操作是原子的,并且提供总线锁定和缓存锁定两个机制来保证复杂内存操作的原子性。

使用总线保证原子性

如果有多个处理器同时对共享变量进行操作,那么共享变量就会被多个处理器同时操作,这样的话,读改写操作就不是原子的。
比如i=1,i++,两个处理器同时进行操作,最后的结果,可能是3,也可能是3.
原因可能是多个处理器同时从各自的缓存中读取变量i,分别进行加1操作,然后分别写入系统内存。
处理器使用总线锁来解决这个问题。当处理器发出LOCK#信号时,其他处理器的请求会被阻塞主,该处理器可以独占共享内存。

使用缓存锁定来保证原子性

锁总线开销还是很大的,锁住了CPU和内存之间的通信。
因为频繁使用的内存会缓存在处理器的L1、L2、L3高速缓存中,原子操作可以在缓存内部完成,同时通过缓存一致性协议,当A处理器修改缓存中的i时,其他处理器不能同时缓存i,即会使得其他处理器中对于共享变量的缓存失效。
这段还不是特别明白,感觉得重新翻一下操作系统,有知道的网友可以留言补充一下。

Java实现原子操作的方式

Java可以使用锁,实现一段代码的原子操作。但这样开销比较大,会引起频繁的上下文切换。
另外一种方式就是使用CAS操作(比较交换)。
CAS算法的过程是比较简单的。它会包含三个参数(V,E,N))。V表示要更新的变量,E表示预期值,N值。当且仅当V等于E值时,才会将V的值设为N,如果V值和E值不同,说明已经有其他线程做了更新,则当前线程什么都不做。
当多个线程同时使用CAS对变量进行操作时,只有一个会胜出并成功更新,其余会失败。失败的线程不会被挂起。
Java中对于基本类型的包装类都有对应的原子操作实现,比如
AtomicBoolean
AtomicInteger

如果拿AtomicInteger为例子,其中的incrementAndGet的实现如下所示,是直接调用了Unsafe类的方法:

 public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

其中var1是传入对象的引用,var2是字段到对象头部的偏移量,方便快速定位,var5是当前值,var5+var4就是期望值,var4传入的是1。
如果是引用类型的话,可以使用AtomicReference。
CAS操作虽好,但它会遇到ABA问题,即一个变量先是A,后来变成了B,在比较时又变回了A,但CAS操作无法感知到这种情况,如果说我们是否可以修改当前值,不仅取决于当前值,还取决于它的变化,那么原有的CAS操作就无能为力了,因为它感知不到。
贴心的JDK为我们提供了AtomicStampedReference,它在对象内部维护了时间戳,当更新数据时,不仅要更新数据,还要更新时间戳。当AtomicStampedReference设置对象值时,对象值以及时间戳都必须满足期望值,写入才会成功。
如果是数组类型的话,JDK提供了AtomicIntegerArray等数组类型的原子类。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70559.html

相关文章

  • Java并发编程艺术二章读书笔记volatile关键字

    摘要:目前看的部分主要是这个关键字。语言提供了,保证了所有线程能看到共享变量最新的值。前缀的指令在多核处理器下会做两件事情将当前处理器缓存行的数据写回到系统内存。 这一章节的话,主要是讲一下在并发操作中常见的volatile、synchronized以及原子操作的相关知识。 目前看的部分主要是volatile这个关键字。 volatile 根据Java语言规范第3版中对volatile的定义...

    szysky 评论0 收藏0
  • Java并发编程艺术二章读书笔记synchronized关键字

    摘要:在之前的文章中学习了关键字,可以保证变量在线程间的可见性,但他不能真正的保证线程安全。线程执行到指令时,将会尝试获取对象所对应的的所有权,即尝试获得对象的锁。从可见性上来说,线程通过持有锁的方式获取变量的最新值。 在之前的文章中学习了volatile关键字,volatile可以保证变量在线程间的可见性,但他不能真正的保证线程安全。 /** * @author cenkailun *...

    GT 评论0 收藏0
  • Java并发编程艺术】第一章读书笔记

    摘要:前言并发编程的目的是让程序跑的更快,但并不是启动更多的线程,这个程序就跑的更快。尽可能降低上下文切换的次数,有助于提高并发效率。死锁并发编程中的另一挑战是死锁,会造成系统功能不可用。 前言 并发编程的目的是让程序跑的更快,但并不是启动更多的线程,这个程序就跑的更快。有以下几种挑战。 挑战及方案 上下文切换 单核CPU上执行多线程任务,通过给每个线程分配CPU时间片的方式来实现这个机制。...

    马忠志 评论0 收藏0
  • 那些年我看过书 —— 致敬我大学生活 —— Say Good Bye !

    摘要:开头正式开启我入职的里程,现在已是工作了一个星期了,这个星期算是我入职的过渡期,算是知道了学校生活和工作的差距了,总之,尽快习惯这种生活吧。当时是看的廖雪峰的博客自己也用做爬虫写过几篇博客,不过有些是在前人的基础上写的。 showImg(https://segmentfault.com/img/remote/1460000010867984); 开头 2017.08.21 正式开启我...

    xiaoqibTn 评论0 收藏0
  • 【J2SE】java并发编程实战 读书笔记( 一、二、三章)

    摘要:发布的对象内部状态可能会破坏封装性,使程序难以维持不变性条件。不变性线程安全性是不可变对象的固有属性之一。可变对象必须通过安全方式来发布,并且必须是线程安全的或者有某个锁保护起来。 线程的优缺点 线程是系统调度的基本单位。线程如果使用得当,可以有效地降低程序的开发和维护等成本,同时提升复杂应用程序的性能。多线程程序可以通过提高处理器资源的利用率来提升系统的吞吐率。与此同时,在线程的使用...

    QLQ 评论0 收藏0

发表评论

0条评论

olle

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<