1. CAS原理
中文名叫比较交换---Compare And Swap
CAS(V,E,N)
V表示要更新的值;
E表示预期值;
N表示新值。
比较原则:当V和E相等时,才把N赋值给V;如果V和E不相等时,放弃当前修改。
1.1 如何保证原子性
基于硬件的汇编指令实现原子性,使用CPU指令保证原子性;
// Unsafe 后门类,用于直接操作内存中的数据private static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); }}private volatile int value;/** * CAS实现 */public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}/** * 使用自旋的方式保证线程安全 */public final int getAndUpdate(IntUnaryOperator updateFunction) { int prev, next; do { prev = get(); next = updateFunction.applyAsInt(prev); } while (!compareAndSet(prev, next)); return prev;}
1.2 CAS的缺点
循环时间太长
有些类限制了自旋的次数
只能保证一个共享变量的原子操作
ABA问题
如果一个值原来是A,变成了B,然后又变成了A,那么在CAS检查的时候会发现没有改变,但是实质上它已经发生了改变,这就是所谓的ABA问题。
使用类AtomicStampedReference来解决ABA问题。
如果当前引用 == 预期值,并且当前标志值 == 预期标志值,则就将当前引用和当前标志值赋值为更新值。
/** * CAS实现 * * @param expectedReference 引用的预期值 * @param newReference 引用的新值 * @param expectedStamp 标志的预期值 * @param newStamp 标志的新值 */ public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) { Paircurrent = pair; return expectedReference == current.reference && expectedStamp == current.stamp && ((newReference == current.reference && newStamp == current.stamp) || casPair(current, Pair.of(newReference, newStamp))); }
2. AQS原理
AQS----AbstractQueuedSynchronizer的缩写
子类定义为非公共内部帮助器类(私有的内部类继承AQS);写锁的时候的一个帮助器,提供获取锁和是释放锁的功能模板。
acquire(int arg) 以独占模式获取对象,忽略中断。
acquireShared(int arg) 以共享模式获取对象,忽略中断。
tryAcquire(arg) 试图在独占模式下获取对象状态。
tryAcquireShared(int arg) 试图在共享模式下获取对象状态
release(int arg) 以独占模式释放对象。
releaseShared(int arg) 以共享模式释放对象
3. 自定义锁
4. 并发工具类
4.1 CountDownLatch(查询航班)
/** * 新建一个latch为3的锁计数器 */CountDownLatch latch = new CountDownLatch(3);/** * 减少计数,直到计数为0,则释放所有线程 */latch.countDown();/** * 导致当前线程等待,直到计数器为0时。 */latch.await();
4.2 CyclieBarries
CyclicBarrier barrier=new CyclicBarrier(8);/** * 在所有参与者都调用 `await` 方法之前,所有线程一直等待。 */barrier.await();
4.3 Semaphore
是一个计数信号量。
/** * 创建Semaphore */Semaphore sp=new Semaphore(5);/** * 从信号量获取一个许可,如果许可不够,会一直将线程阻塞,否则中断线程。 */sp.acquire();/** * 释放一个许可,将其返回给信号量。 */sp.release();