多线程中的原子性问题及CAS

线程中的原子性:

所谓原子性,就是一系列操作,要么全部执行,要么全部不执行,不会存在只执行一部分的情况,举个例子,对于i++这个操作,那么这个自增的操作完整步骤分三步,读i的值----改i的值---写回i的值;如果不对i进行加锁,在执行这三步的时候,比如说改i的值的的时候,线程被打断了,那么就会出现预期结果不一致的问题,比如说在这个i++的操作中,读这一步的操作读到了i的值为1,然后在改之前,线程中断,i的值被其他线程改成了0,那此时,线程恢复,对i自增,把值改成2,写回的i的值为2,这就不合理,因为i的值被改成0了,对0自增一次却变成了2。

CAS操作

上面提到的原子性问题,在多线程环境下影响很大,虽然给变量加上volatile可以解决共享变量的读问题-----即内存可见性问题。(加了volatile的变量每次读值都会直接从主存读值,而不从自己的线程本地读值),就是说读--改---写这个步骤中,volatile可以解决读的问题,但是改和写这两个步骤的原子性还是无法保证,步骤整体的原子性依旧无法保证,还是存在在写或者改的时候被打断的情况。

所以为解决这个问题,就出现了CAS,CAS的全称是compare and swap,CAS操作的整个步骤就是,拿到自己读取的值和内存的值比较,如果这两个值相同就说明没被其他线程改过,进行更新操作。如果不同就什么都不做,继续从内存中读取值,比较值,直到更新成功为止。这样的一个循环的过程。有点类似与乐观锁,示意图如下

ABA问题

但是CAS操作也会面临一个问题,就是ABA问题,ABA问题:有一个变量值为0,A线程拿到的值为0,然后要把他修改成1,然后改的过程中,其他线程B拿到这个变量值0.然后改成了2,最后又改回了0,或者有一个C线程把他改成了0,,虽然值为一样都为0,但是这个0已经不是原来的0了,这就是ABA问题。

举个形象的例子就是,你女朋友跟你分手了,然后跟别人在一起,最后又跟你复合了。然后你们俩最后还是和之前一样在一起。但是感情已经不是原来的感情了。总结ABA问题就是: == 其他线程修改数次的最后值和原值相同 ==

那么一般如何解决ABA问题,一般可以读值的时候带一个版本号,更新之前比较版本号,版本号一致则说明是同一个值,更新完毕之后,版本号+1。也是和乐观锁很像

CAS如何解决原子性问题

那么cas是怎么保证一致性的,会不会出现这种情况,在线程进行值修改的那个过程中,在执行修改指令的前一条指令执行之前。执行权被剥夺,值被其他的线程变了?

CAS 底层的汇编实现 lock cmpxchg 指令 这个指令在对一块区域的值进行修改的时候,其他的线程和cpu不许打断这个指令,cas的一致性就是基于这条命令。lock指令非常重要,后面的synchronized都是基于这个lock指令,确保在执行lock指令的时候,直接锁住总线,不允许任何线程打断这个指令

posted @ 2020-05-10 15:37  穿黑风衣的牛奶  阅读(600)  评论(0编辑  收藏  举报