之的作用是什么?
在单线程的程序里,使用原子操作能保证顺序执行——只有加锁和解锁两个操作步骤,因为操作系统内核实现时,这些步骤都是原子的(Linux 3.6之后的版本都已经实现了操作系统的自锁功能);而在多线程的程序里,由于加了内存屏障,操作系统仍然可以保证各从线程对变量的操作是原子的,只不过每次操作之间的序并非必然为递增的。
而CAS操作则是基于上面的这些特性来实现的:只要两次操作的上下文(包括变量值和自己保存的标识)保持一致,就能保证是原子的,否则要么不更新,要么全部刷新回原来的值。因此CAS比原生C语言里的互斥锁效率更高,在多线程环境下更加合适。 而ABA问题则是在上述基础上更进一步的问题——这个问题出现的场景一般是当变量被多个线程同时修改但只有一个线程能够成功写入(也就是所谓的写冲突)的情况下。为了避免这种问题,CAS需要添加额外的操作——先读出变量的值,如果与原值不一致,说明之前有其它线程写了进去,这样第一次CAS操作就会失败,之后重新尝试直到成功或者达到超时限制。
这里重试的过程本身也是原子和串行的,因此在Java等不支持并行编程的语言里,只能一次一次的轮询直至达成目的。而在C/C++这类支持并发编程的语言里,则可以借助临界区、内存屏障以及CPU的硬中断来实现。当然,这种循环重试的结构本身就是有代价的,它会在循环内阻塞住当前线程,导致时间片被浪费以至于整个进程的效率下降。 在现代的编译器里,对于整数类型(int、short之类)的操作系统内部都会进行优化从而无需进行ABA检查,所以这种结构在实际应用中并不会遇到。但对于字符串这样的复合数据类型,情况就有所不同了。