Java并发编程的艺术——原子操作的实现原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lhc2207221755/article/details/83933944

原子操作的定义

atomic,表面上,指的是不能进一步分割的最小粒子。借鉴我们在数据库中学的事务的概念,atomic operation,意为——不可被中断的一个或者一系列操作。

处理器如何实现原子操作

使用总线锁保证原子性

如果多个处理器同时对共享变量进行读改写操作,例如,i++操作,那么共享变量会被多个处理器同时操作,这样读改写操作就不是原子的了。例如,cpu1将共享变量i=1读到自己的缓存,与此同时,cup2将i=1也读入了了自己的缓存,cup1 和cpu2在各自读到的i=1的基础上进行++操作,之后再写回系统内存中,此时i=2,两次自加操作,并没有让i=3。
要想保证读写改一个变量的操作是原子的,就必须保证CUP1读写改共享变量的时候,CUP2不能操作缓存了该共享变量内存地址的缓存。
处理器使用总线锁就是来解决这个问题的。总线锁就是使用处理器提供的一个LOCK信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占共享内存。

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

在同一时刻,我们只需要保证对某个内存地址的操作是原子性即可,但总线锁定把CUP和内存之间的通信锁住了,这使得锁定期间,其他处理器不能操作其他内存地址的数据,所以总线锁定的开销比较大,目前处理器在某些场合下使用缓存锁定代替总线锁定来进行优化。

Java如何实现原子操作

使用循环CAS实现原子操作

JVM中的CAS操作正是利用了处理器提供的CMPXCHG指令来实现的。自旋CAS实现的基本思路就是循环进行CAS操作直到成功为止。

cas实现原子操作的三大问题

  • ABA问题
    cas需要再操作值的时候,检查值有没有发生变化,如果没有发生变化则更新。但是一个值,如果原来为A,变成了B,又变成了A,那么使用CAS进行compare and set的时候,会发现它的值根本没变化过,但实际上是变化过的。ABA问题的解决思路就是使用版本号,1A->2B->3C。在Atomic包中(above 1.5),提供了一个AtomicStampedReference来解决ABA问题。
  • 循环时间开销大
    自选CAS如果长时间不成功,会给CUP带来非常大的执行开销。
  • 只能保证一个共享变量的原子操作
    (above 1.5)提供了AtomicReference类来保证引用对象之间的原子性,可以将多个变量放在一个对象里进行cas操作。

使用锁实现原子操作

锁机制保证了只有获得锁的线程才能操作锁定的内存区域。JVM内,实现了很多锁机制,有偏向锁,轻量级锁,互斥锁。除了偏向锁,JVM实现锁的方式都使用了循环CAS,即当一个线程想进入同步快的时候使用循环CAS的方式来获取锁,当它退出同步快的时候使用CAS释放锁。

猜你喜欢

转载自blog.csdn.net/lhc2207221755/article/details/83933944
今日推荐