Javaの並行処理アトミックとAQSの高度な知識

まず、原子、原子クラスについて教えてください

文字通り、アトミックので、ここで我々は複数のスレッドで一緒に行った場合でも、中断することができないアクションを参照して、原子を意味する、一度、開始アクションは、他のスレッドによって妨害されることはありません。

 

第二に、原子型パッケージJUCクラス4?

基本タイプ

基本的なタイプのアトミック更新を使用します

  • AtomicInteger:クラス整数原子
  • AtomicLong:長期アトミック
  • AtomicBoolean:ブール - アトム

 

配列型

アトミックな方法の使用は、配列の要素を更新します

  • AtomicIntegerArray:整数アレイ原子クラス
  • AtomicLongArray:クラス原子長整数配列
  • AtomicReferenceArray:参照型原子クラスのアレイ

 

参照型

  • AtomicReference:クラス参照型原子
  • AtomicStampedRerence:アトミック・ベースのフィールドに参照型原子を更新します
  • AtomicMarkableReference:参照型原子マーク位置を更新

 

オブジェクトタイプの属性変更

  • AtomicIntegerFieldUpdater:アトミック更新フィールド整形アップデータ
  • AtomicLongFieldUpdater:アトミック更新アップデータ長整数フィールド
  • AtomicStampedReference:参照型のアトミック更新のバージョン番号。このような参照は、データを更新するために使用することができる整数値と原子溶液のバージョン番号に関連付けられている、ABAはCASアトミック更新を使用する場合に発生する可能性のある問題を解決することができます。

 

AtomicIntegerの使用について第三に、話

クラス共通メソッドのAtomicInteger

公共の 最終的な int型 GET()// の現在の値を取得
公共 決勝 のint getAndSetを(int型 newValueに)// 現在の値を取得し、新たな価値を設定
パブリック 決勝 のint getAndIncrementを()// 現在の値を取得し、インクリメント
公共 ファイナル int型 getAndDecrement()// 現在の値を取得し、デクリメント
公共 最終 int型 getAndAddを(int型デルタ)// 現在の値と期待値プラス取得
ブールのcompareAndSetの(intは期待して、int型の更新を)// 入力の値であればアトミック入力値(更新)に値を設定した期待値に等しいです
公共の 最終 無効 lazySet(int型 newValueに)// 最終セットnewValueに、まだ古い値を読み取ることができた後、使用lazySetの設定は、短い時間内に他のスレッドをもたらすことができる後。

AtomicIntegerを使用して実施例クラス

AtomicIntegerを使用した後、インクリメントしない()メソッドは、スレッドの安全性を確保するためにロックすることができます。

クラスAtomicIntegerTest {
       プライベートのAtomicIntegerカウント=の新しい新規のAtomicIntegerの();
       // のAtomicIntegerを使用した後、この方法は、スレッドの安全性を達成することができる、ロックを必要としません。
      公共 ボイドINCREMENT(){ 
          count.incrementAndGet(); 
      } 

      公共 のint 同様にgetCount(){
           戻りcount.get(); 
      } 
}

 

第四に、クラスのAtomicIntegerの原則

AtomicIntegerスレッドセーフな原理は簡単分析であります

AtomicIntegerクラス源部:

// 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 我已经到达了屏障,然后当前线程被阻塞。

 

おすすめ

転載: www.cnblogs.com/reformdai/p/11113323.html