美文网首页并发编程
3. java并发编程之-原子操作的实现原理

3. java并发编程之-原子操作的实现原理

作者: wangxiaowu241 | 来源:发表于2019-03-20 00:00 被阅读0次

    原子操作,意为不可分割、不可中断的操作。单处理器实现原子操作很简单,因为就一个处理器,不会有并发问题,那么在多处理器中要实现原子操作就比较复杂。

    处理器如何实现原子操作?

    首先,处理器会自动保证对对单个字节的操作是原子性的,即对一个字节在某个时间点上只有一个读或者写操作在进行。那么多个字节呢?16位/32位/64位呢?有些处理器也是自动可以保证同一缓存行上的数据操作是原子的,但是复杂的数据,比如说跨缓存行、跨总线的数据,就无法自动保证了。

    这时一般会有两种方式来实现原子操作:总线锁和缓存锁。

    总线锁

    我们都知道,CPU、内存之间的交互是要通过总线的,那么通过总线锁就可以保证同一时间点只会有一个CPU在操作内存上的变量,从而保证了共享变量的原子性。

    缓存锁

    由于CPU和内存的操作都会通过总线,为了某个变量保证原子性而使用总线锁,那么CPU也不能读取内存上的其他变量了,开销较大。因此,处理器在某些场合使用缓存锁来代替总线锁进行优化。

    随着科技的发展,CPU的处理速度越来越快,与之对应的内存的读写速度就远远比不上CPU的处理速度。为了加快CPU的处理速度,从二八定律出发,百分之八十的时间都是读取百分之二十的数据。在CPU和内存中间加上高速缓存,从而提高任务的处理速度。处理器会把最近读取以及常用的变量加载到处理器的高速缓存中,这就是缓存锁实现的依据。

    缓存锁:操作CPU高速缓存中的数据,并在LOCK期间被锁定,当它在LOCK中执行写操作时,CPU会通过缓存一致性原则来阻止同时有两个及以上的处理器修改缓存行的数据。

    但是以下情况不会使用缓存锁:

    1. 当操作的数据无法缓存到处理器的高速缓存内部时或操作的数据跨多个缓存行,只能使用总线锁来保证原子性。
    2. 有些处理器不支持缓存锁。

    Java中实现原子操作

    Java中一般通过锁和循环CAS来实现原子操作。

    锁的实现原理同上面类似,即同一时间只能有一个线程操作共享变量,从而保证操作的原子性。

    而循环CAS操作也可以用来保证原子操作,java的并发包中就大量运用了循环CAS来实现原子操作。

    但是CAS来实现原子操作有三大问题:

    1. ABA问题

      CAS在操作时会先比较现在的值与预期值是否一致,一致才会修改。但当变量原先值为A,后来修改为B,再修改为A,这个时候在进行操作,会发现它仍和预期的值一致,但实际上已经有其他线程操作过数据了。解决ABA问题的解决办法就是加版本号,A->B->A就变成了 1A->2B->3A

    2. 循环时间长开销大

      在竞争激烈的时候CAS始终失败,就会导致始终在自旋,CPU一致在运行。这里就可以使用循环CAS+超时机制来避免长时间自旋。

    3. 只能保证一个共享变量的原子操作

    相关文章

      网友评论

        本文标题:3. java并发编程之-原子操作的实现原理

        本文链接:https://www.haomeiwen.com/subject/vdfamqtx.html