Java Concurrency advanced knowledge of Atomic and AQS

First, tell us about Atomic atomic class

Literally, Atomic is meant atoms, so here we refer to an action can not be interrupted, even when performed together in multiple threads, an action once started, will not be disturbed by other threads.

 

Second, the atom type package which JUC class 4?

basic type

Using atomic update the basic types of

  • AtomicInteger: Class Integer atoms
  • AtomicLong: Long-atomic
  • AtomicBoolean: Boolean - Atom

 

Array type

The use of atomic way to update an element of the array

  • AtomicIntegerArray: integer array atoms class
  • AtomicLongArray: Class atoms long integer array
  • AtomicReferenceArray: an array of reference type atom class

 

Reference types

  • AtomicReference: class reference type atom
  • AtomicStampedRerence: update the reference type atom in the field of atomic-based
  • AtomicMarkableReference: update the reference type atom marked position

 

Object type attribute modification

  • AtomicIntegerFieldUpdater: atomic update field shaping updater
  • AtomicLongFieldUpdater: atomic update updater long integer field
  • AtomicStampedReference: atomic update version number with a reference type. Such references are associated with the integer value it can be used to update data and the version number of atoms solution, ABA can solve problems that may arise when using CAS atomic updates.

 

Third, talk about the use of AtomicInteger

Class common method AtomicInteger

public  Final  int GET () // Get the current value of the 
public  Final  int getAndSet ( int newValue) // Get the current value and set the new value of the 
public  Final  int getAndIncrement () // Get the current value and increment 
public  Final  int getAndDecrement () // get the current value and decrement 
public  Final  int getAndAdd ( int Delta) // get the current value and the expected value plus the 
Boolean of compareAndSet ( int expect, int Update) // if the value of the input equal to the expected value atomically set the value to the input value (Update)
public  Final  void lazySet ( int newValue) // final set newValue, after use lazySet settings may result in other threads within a short time after can still read the old value.

Example classes using AtomicInteger

After using AtomicInteger, not to increment () method can also be locked to ensure thread safety.

class AtomicIntegerTest {
       Private of AtomicInteger COUNT = new new of AtomicInteger ();
       // after use of AtomicInteger, this method does not require locking, thread safety can be achieved. 
      public  void INCREMENT () { 
          count.incrementAndGet (); 
      } 

      public  int getCount () {
           return count.get (); 
      } 
}

 

Fourth, the principle of class AtomicInteger

AtomicInteger thread-safe principle is simple analysis

AtomicInteger class source part:

// setup to use Unsafe.compareAndSwapInt for updates(更新操作时提供“比较并替换”的作用)
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;

 

五、AQS介绍

AQS 的全称是 AbstractQueueSynchronizer,这个类在 java.util.concurrent.locks 包下面。

AQS 是一个用来构建锁个同步器的框架,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的 ReentrantLock,Semaphore,其他诸如 ReentrantReadWriteLock,SynchronousQueue,FutureTask 等等皆是基于 AQS 的。当然,我们也能自己利用 AQS 非常容易地构造出符合我们自己需求的同步器。

六、AQS 原理分析

1、AQS 原理概览

AQS 核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制 AQS 是用 CLH 队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

CLH(Craig,Landin,and Hagersten) 队列是一个虚拟的双向队列 (虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS 是将每条请求共享资源的线程封装成一个 CLH 锁队列的一个结点(Node)来实现锁的分配。

看个 AQS 的原理图:

AQS 使用一个 int 成员变量来表示同步状态,通过内置的 FIFO 队列来完成获取资源线程的排队工作。AQS 使用 CAS 对该同步状态进行原子操作实现对其值的修改。

private volatile int state;//共享变量,使用 volatile 修饰保证线程可见性

状态信息通过 protected 类型的 getState,setState,compareAndSetState 进行操作。

// 返回同步状态的当前值
protected final int getState() {  
    return state;
}
// 设置同步状态的值
protected final void setState(int newState) { 
    state = newState;
}
// 原子地(CAS 操作)将同步状态值设置为给定值 update 如果当前同步状态的值等于 expect(期望值)
protected final boolean compareAndSetState(int expect, int update) {
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

2、AQS 对资源的共享方式

AQS 定义两种资源共享方式

  1. Exclusive(独占):只有一个线程能执行,如 ReentrantLock。又可分为公平锁和非公平锁:
    • 公平锁:按照线程在对列中的排序,先到者先拿到锁;
    • 非公平锁:当线程要获取锁时,无视队列顺序直接抢锁,谁抢到就是谁的。
  2. Share(共享):多个线程可同时执行,如 Semaphore/CountDownLatch。Semphore、CountDownLatch、CycliBarrier、ReaderWriteLock。

ReentrantReadWriteLock 可以看成是组合式,因为 ReentrantReadWriteLock 也就是读写锁允许多个线程同时对某一资源进行读。

不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源 state 的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS 已经在顶层实现好了。

3、AQS 底层使用了模板方法模式

同步器的设计是基于模板方法模式的,如果需要自定义同步器一般的方式是这样的(模板方法模式是很经典的一个应用):

  1. 使用者继承 AQS 并重写指定的方法。(这些重写方法很简单,无非是对于共享资源 state 的获取和释放)
  2. 将 AQS 组合在自定义组件的实现中,并调用其模板方法,而这些模板方法会调用使用者重写的方法。

这和我们以往通过实现接口的方法有很大区别,这是模板方法模式很经典的一个应用。

AQS 使用了模板方法模式,自定义同步器时需要重写下面几个 AQS 提供的模板方法:

isHeldExclusively()//该线程是否正在独占资源。只有用到 condition 才需要去实现它。
tryAcquire(int)//独占方式。尝试获取资源,成功则返回 true,失败则返回 false。
tryRelease(int)//独占方式。尝试释放资源,成功则返回 true,失败则返回 false。
tryAcquireShared(int)//共享方式。尝试获取资源。负数表示失败;0 表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回 true,失败则返回 false。

默认情况下,每个方法都抛出 UnsupportedOperationException。这些方法的实现必须是内部线程安全的,并且通常应该简短而不是阻塞。AQS 类中的其他方法都是 final ,所以无法被其他类使用,只有这几个方法可以被其他类使用。

以 ReentrantLock 为例,state 初始化为 0,表示未锁定状态。A 线程 lock() 时,会调用 tryAcquire() 独占该锁并将 state+1。此后,其他线程再 tryAcquire() 时就会失败,直到 A 线程 unlock() 到 state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A 线程自己是可以重复获取此锁的(state 会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证 state 是能回到零态的。

再以 CountDownLatch 以例,任务分为 N 个子线程去执行,state 也初始化为 N(注意 N 要与线程个数一致)。这 N 个子线程是并行执行的,每个子线程执行完后 countDown() 一次,state 会 CAS(Compare and Swap) 减 1。等到所有子线程都执行完后 (即 state=0),会 unpark() 主调用线程,然后主调用线程就会从 await() 函数返回,继续后余动作。

一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现 tryAcquire-tryRelease、 tryAcquireShared-tryReleaseShared 中的一种即可。但 AQS 也支持自定义同步器同时实现独占和共享两种方式,如 ReentrantReadWriteLock。

 

七、AQS 组件总结

  • Semaphore(信号量)-允许多个线程同时访问:synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源,Semaphore 可以同时制定多个线程同时访问某个资源。
  • CountDownLatch(倒计时器):CountDownLatch 是一个同步工具类,用来协调多个线程之间的同步。这个工具通常用来控制线程等待,它可以让某一个线程等到直到倒计时结束,再开始执行。
  • CyclicBarrier(循环栅栏):CylicBarrier 和 CountDownLatch 非常类似,他也可以实现线程间的技术等待,但是它的功能比 CountDownLatch 更加复杂和强大。主要应用场景和 CountDownLatch 类似。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时才会开门,所有被屏障拦截的线程才会继续干活。CylicBarrier 默认构造方法是 CylicBarrier(int particles),其参数表示屏障拦截的线程数,每个线程调用 await() 方法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程被阻塞。

 

Guess you like

Origin www.cnblogs.com/reformdai/p/11113323.html