百色建设网站,网站建设的宽带指标要求,沈阳网站关键词优化排名,导购网站 转化率Java中的原子性操作
所谓原子性操作#xff0c;是指执行一系列操作时#xff0c;这些操作要么全部执行#xff0c;要么全部不执行#xff0c;不存在只执行其中一部分的情况。
在设计计数器时一般都先读取当前值#xff0c;然后1,再更新。
这个过程是读—改—写的过程是指执行一系列操作时这些操作要么全部执行要么全部不执行不存在只执行其中一部分的情况。
在设计计数器时一般都先读取当前值然后1,再更新。
这个过程是读—改—写的过程如果不能保证这个过程是原子性的那么就会出现线程安全问题。
如下代码是线程不安全的因为不能保证value是原子性操作。 那么如何才能保证多个操作的原子性呢?最简单的方法就是使用synchronized关键字进行同步。 使用synchronized关键字的确可以实现线程安全性即内存可见性和原子性但是synchronized是独占锁没有获取内部锁的线程会被阻塞掉而这里的getCount方法只是读操作多个线程同时调用不会存在线程安全问题。
但是加了关键字synchronized后同一时间就只能有一个线程可以调用这显然大大降低了并发性。
你也许会问既然是只读操作那为何不去掉getCount方法上的synchronized关键字呢?其实是不能去掉的别忘了这里要靠synchronized来实现value的内存可见性。
那么有没有更好的实现呢?答案是肯定的下面将讲到的在内部使用非阻塞CAS算法实现的原子性操作类AtomicLong就是一个不错的选择。
Java中的CAS操作
在Java中锁在并发处理中占据了一席之地但是使用锁有一个不好的地方就是当一个线程没有获取到锁时会被阻塞挂起这会导致线程上下文的切换和重新调度开销。
Java提供了非阻塞的volatile关键字来解决共享变量的可见性问题这在一定程度上弥补了锁带来的开销问题但是volatile只能保证共享变量的可见性不能解决读一改一写等的原子性问题。
CAS即Compare and Swap,其是JDK提供的非阻塞原子性操作它通过硬件保证了比较一更新操作的原子性。JDK里面的Unsafe类提供了一系列的compareAndSwap*方法下面以compareAndSwapLong方法为例进行简单介绍。
boolean compareAndSwapLong(Object obj,long valueOffset,long expect,long update)方法其中compareAndSwap的意思是比较并交换。CAS有四个操作数分别为对象内存位置、对象中的变量的偏移量、变量预期值和新的值。其操作含义是如果对象obj中内存偏移量为valueOffset的变量值为expect,则使用新的值update替换旧的值expect。这是处理器提供的一个原子性指令。
关于CAS操作有个经典的ABA问题具体如下假如线程I使用CAS修改初始值为A的变量X,那么线程I会首先去获取当前变量X的值(为A),然后使用CAS操作尝试修改X的值为B,如果使用CAS操作成功了那么程序运行一定是正确的吗?其实未必这是因为有可能在线程I获取变量X的值A后在执行CAS前线程Ⅱ使用CAS修改了变量X的值为B,然后又使用CAS修改了变量X的值为A。所以虽然线程I执行CAS时X的值是A,但是这个A已经不是线程I获取时的A了。这就是ABA问题。
ABA问题的产生是因为变量的状态值产生了环形转换就是变量的值可以从A到B,然后再从B到A。
如果变量的值只能朝着一个方向转换比如A到B,B到C,不构成环形就不会存在问题。
JDK中的AtomicStampedReference类给每个变量的状态值都配备了一个时间戳从而避免了ABA问题的产生。