其中在java中使用CAS底层借助 Unsafe 来操作,实现非阻塞的修改变量值,CAS是使用了硬件指令保证原子性
每次进行compareAndSwap方法时,需要cas有3个参数
内存地址V,
旧的预期值E,
要修改的新值N
当 E == V 时,修改 V 值为 N,返回true
当 E != V 时,修改失败,返回false
AtomicBoolean,AtomicInteger,AtomicLong 等都是使用CAS来实现的。但是没有解决ABA问题。
什么是ABA问题
一个线程获取值为a,另一个线程将数值由a改成了b,接着又改成了a,此时线程b的CAS操作认为是没有变化,但其实已经变化过了,这种过程就叫ABA问题。
CAS的ABA问题解决
、ABA问题
> 解决方法:JAVA中提供了AtomicStampedRefernce/AtomicMarkableReference来处理会发生
ABA问题的场景,主要是在对象中额外再增加一个标记来标识对象是否有过变更。
2、CAS应用场景隐含竞争是短暂的,否则不断的自旋尝试会过度消耗CPU> 解决方法加入超时设置
3、CAS只能保证一个共享变量的原子操作,解决方法是使用锁或者合并多个变量> AtomicReference提供了以无锁方式访问共享资源的能力
CAS的总结
AtomicInteger、AtomicBoolean、AtomicLong、AtomicReference这些原子类型,它们无一例外都采用了基于volatile 关键字 +CAS 算法无锁的操作方式来确保共享数据在多线程操作下的线程安全性。- volatile关键字保证了线程间的可见性,当某线程操作了被volatile关键字修饰的变量,其他线程可以立即看到该共享变量的变化。
- CAS算法,即对比交换算法,是由UNSAFE提供的,实质上是通过操作CPU指令来得到保证的。CAS算法提供了一种快速失败的方式,当某线程修改已经被改变的数据时会快速失败。
- 当CAS算法对共享数据操作失败时,因为有自旋算法的加持,对共享数据的更新终究会得到计算。总之,原子类型用自旋+CAS的无锁操作保证了共享变量的线程安全性和原子性。
AQS模型
AbstractQueuedSynchronizer抽象队列同步器:AQS是JDK下提供的一套用于实现基于FIFO等待队列的阻塞锁
和相关的同步器的一个同步框架。AQS维护了一个volatile int state(代表共享资源)和一个FIFO线程等待
队列(多线程争用资源被阻塞时会进入此队列)
抽象队列同步器中包括两个部分:临时资源和一个FIFO的CLH阻塞队列.
AQS的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资
源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,
这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
内部使用AQS的例子:以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用
tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到
state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁
的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能
回到零态的。