CAS
比較して交換(比較と交換)/スピン/スピンロック/ロックなし(ウェイトロックなし)
完了するまでループ操作と連携することが多いため、操作の種類を指します
cas(v、a、b)、変数v、期待値a、修正値b
ABAの問題、あなたのガールフレンドはあなたが去った時間中に他の誰かを経験しました、スピンはあなたがアイドリングを待っています、彼女があなたを受け入れるまで待ちます
ソリューション(バージョン番号AtomicStampedReference)、基本タイプの単純な値はバージョン番号を必要としません
CASの典型的なアプリケーションは
java.util.concurrent.atomicパッケージの下のクラスで、主にCASオペレーションを使用して実装されます(例:AtomicInteger.java、AtomicBoolean、AtomicLong)。以下は、これらのアトミッククラスの実装を説明するAtomicInteger.javaの部分的な実装です。
AQS:
AQS(AbstractQueuedSynchronizer)、AQSは、JDKの下で提供される一連の同期フレームワークで、FIFO待機キューに基づいてブロッキングロックと関連するシンクロナイザーを実装します。この抽象クラスは、アトミックint値を使用して状態を表すことができるシンクロナイザーの基本クラスとして機能するように設計されています。CountDownLatchクラスと同様のソース実装を見た場合、AbstractQueuedSynchronizerを継承する内部クラスSyncがあることがわかります。CountDownLatchはAQSフレームワークに基づくシンクロナイザーであり、JUCの下には多くの同様のシンクロナイザーがあります。(例:セマフォ)
AQS原則の概要
AQSの核となる考え方は、要求された共有リソースがアイドル状態の場合、現在リソースを要求しているスレッドが有効なワーカースレッドとして設定され、共有リソースがロック状態に設定されるということです。要求された共有リソースが占有されている場合は、スレッドブロックの待機メカニズムと、起動時にロックの割り当てが必要です。このメカニズムAQSは、CLHキューロックで実装されます。つまり、一時的にロックを取得できないスレッドがキューに追加されます。
CLH(Craig、Landin、およびHagersten)キューは、仮想双方向キューです(仮想双方向キューにはキューインスタンスはなく、ノード間の関連付けのみがあります)。AQSは、共有リソースを要求する各スレッドをCLHロックキューにカプセル化して、ロック割り当てを実装します。
AQS(AbstractQueuedSynchronizer)の概略図:
AQSはintメンバー変数を使用して同期ステータスを示し、組み込みのFIFOキューを通じてリソース取得スレッドのキューイング作業を完了します。AQSはCASを使用して同期状態に対してアトミック操作を実行し、その値を変更します。
/**
* The synchronization state.
*//共享变量,使用volatile修饰保证线程可见性
*/
private volatile int state;
/**
* Returns the current value of synchronization state.
* This operation has memory semantics of a {@code volatile} read.
* @return current state value
*/
protected final int getState() {
return state;
}
/**
* Sets the value of synchronization state.
* This operation has memory semantics of a {@code volatile} write.
* @param newState the new state value
*/
protected final void setState(int newState) {
state = newState;
}
/**
* Atomically sets synchronization state to the given updated
* value if the current state value equals the expected value.
* This operation has memory semantics of a {@code volatile} read
* and write.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that the actual
* value was not equal to the expected value.
* 使用CAS底层底层实现进行修改,底层为本地native方法,是c++书写的,
* 底层c++ LOCK_IF_MP(mp) 指令:lock cmpxchg 指令
* lock指令在执行后面指令的时候锁定一个北桥信号,实现原子性
* 多线程情况下,与CPU交互的底层指令会加lock锁,
*/
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
AQSが
リソースを共有するAQSは、2つのリソース共有方法を定義します。
排他的:ReentrantLockなど、実行できるスレッドは1つだけです。フェアロックとアンフェアロックに分けることができます:
フェアロック:キュー内のスレッドのキューイング順序に従って、最初のスレッドは最初にロックを取得します//ロック();メソッド
アンフェアロック:スレッドがロックを取得する場合、キューを無視しますロックを取得するために直接注文し、誰がそれを取得するか// tryLock()メソッド
共有:Semaphore / CountDownLatchなどの複数のスレッドを同時に実行できます。セマフォ、CountDownLatch、CyclicBarrier、ReadWriteLock。
ReentrantReadWriteLockは、複数のスレッドが同時にリソースを読み取ることができる読み取り/書き込みロックであるため、ReentrantReadWriteLockは組み合わせとして見ることができます。
AQSの最下層では、テンプレートメソッドパターンを使用します
。シンクロナイザーのデザインは、テンプレートメソッドパターンに基づいています。シンクロナイザーをカスタマイズする必要がある場合、一般的な方法は次のとおりです。
ユーザーはAbstractQueuedSynchronizerを継承し、指定されたメソッドをオーバーライドします。(これらの書き換えメソッドは非常に単純で、共有リソースの状態の取得と解放にすぎません。)
カスタム同期コンポーネントの実装でAQSを組み合わせて、それらのテンプレートメソッドを呼び出します。これらのテンプレートメソッドは、ユーザーによって書き換えられたメソッドを呼び出します。
AQSはテンプレートメソッドモードを使用します。シンクロナイザーをカスタマイズするときは、AQSが提供する次のテンプレートメソッドを書き換える必要があります。
isHeldExclusively()//该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire(int)//独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int)//独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int)//共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true,失败则返回false。
デフォルトでは、各メソッドはUnsupportedOperationExceptionをスローします。これらのメソッドの実装は、内部的にスレッドセーフである必要があり、通常はブロックするのではなく短くする必要があります。AQSクラスの他のメソッドは最終的なものであるため、他のクラスでは使用できません。他のクラスで使用できるのはこれらのメソッドのみです。
ReentrantLockを例にとると、状態は0に初期化され、ロック解除状態を示します。Aスレッドロック()の場合、tryAcquire()を呼び出してロックと状態+ 1を独占します。その後、他のスレッドはtryAcquire()で失敗します。Aスレッドがロック解除()して状態= 0(つまり、ロックを解除)になるまで、他のスレッドはロックを取得する機会があります。もちろん、ロックを解放する前に、Aスレッド自体が繰り返しロックを取得できます(状態は累積されます)。これは再入可能の概念です。ただし、状態をゼロ状態に戻すことができるように、何回解放する必要があるかに注意してください。
CountDownLatchを例にとると、タスクは実行するためにN個のサブスレッドに分割され、状態もNに初期化されます(Nはスレッド数と一致している必要があります)。N個のサブスレッドが並列に実行され、各サブスレッドが実行された後、countDown()が1回実行され、状態が1 CAS(比較およびスワップ)削減されます。すべてのサブスレッドが実行された後(状態= 0)、メインの呼び出しスレッドはパーク解除()になり、メインの呼び出しスレッドはawait()関数から戻って残りのアクションを続行します。
一般的に言って、カスタムシンクロナイザーは排他的メソッドまたは共有メソッドのいずれかであり、tryAcquire-tryReleaseおよびtryAcquireShared-tryReleaseSharedのいずれかを実装するだけで済みます。ただし、AQSはカスタムシンクロナイザーもサポートし、ReentrantReadWriteLockなどの排他的メソッドと共有メソッドの両方を実現します。
AQSコンポーネントの概要
セマフォ(セマフォ):複数のスレッドが同時にアクセスできるようにします。同期とReentrantLockのどちらも、一度に1つのスレッドのみがリソースにアクセスできるようにします。セマフォ(セマフォ)は、リソースに同時にアクセスする複数のスレッドを指定できます。
CountDownLatch(カウントダウンタイマー):CountDownLatchは、複数のスレッド間の同期を調整するために使用される同期ツールクラスです。このツールは通常、スレッドの待機を制御するために使用されます。実行を開始する前に、カウントダウンが終了するまでスレッドを待機させることができます。
CyclicBarrier(Cyclic Barrier):CyclicBarrierはCountDownLatchと非常によく似ており、スレッド間の技術的な待機も実現できますが、その機能はCountDownLatchよりも複雑で強力です。主なアプリケーションシナリオは、CountDownLatchに似ています。CyclicBarrierは文字通りCyclic Barrierを意味します。スレッドのグループがバリア(同期ポイントとも呼ばれる)に到達したときにブロックすることを許可する必要があります。最後のスレッドがバリアに到達するまでバリアは開きません。バリアによってブロックされたすべてのスレッドは引き続き機能します。CyclicBarrierのデフォルトの構築メソッドはCyclicBarrier(intパーティ)であり、そのパラメーターはバリアーによってインターセプトされたスレッドの数を示します。各スレッドはawait()メソッドを呼び出して、CyclicBarrierにバリアーに到達したことを伝え、現在のスレッドはブロックされます。
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
/** Synchronizer providing all implementation mechanics */
//tryLock()和try()都是继承内部类Sync,
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
而Sync实现AbstractQueuedSynchronizer类
ReentrantLock如何处理线程中断的?
AbstractQueuedSynchronizer的parkAndCheckInterrupt()
阻塞队列使用的LockSupport.park();
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
因为LockSupport.park(),无法响应Thread.interrupt(); 所以当unpark()后使用Thread.interrupted()来判断线程是否有中断过。如果中断过整个唤醒的线程在外层方法会继续执行一次中断,详情源码如下:
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
tryLockとロックの違い
1. TryLockは、受信したかどうかに関係なく直接戻ります。ロックを取得できない場合は、永久に待機します。
2. tryLockを中断できます。
tryLockをロック();
ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.tryLock();
public boolean tryLock() {
//调用SYNC的非公平锁方法,去尝试获得锁
return sync.nonfairTryAcquire(1);
}
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
int c = getState();
//如果当前state为0则表示未被加锁
if (c == 0) {
//调用CAS方法,将state的状态从未加锁0改为加锁状态1,多线程情况下,可能会出现加锁失败
if (compareAndSetState(0, acquires)) {
//加锁成功,则把当前线程设置为,当前独占锁的拥有者
setExclusiveOwnerThread(current);
//并返回
return true;
}
}
//如果当前锁的拥有者是当前线程
else if (current == getExclusiveOwnerThread()) {
//则将state加1,表示再次加锁,释放锁时候,只有当state值为0,才表示释放锁完成
int nextc = c + acquires;
//当nextc小于0,则表示超过了,锁的最大计数器
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//设置state为nextc,并返回true
setState(nextc);
return true;
}
//否则返回false
return false;
}
ロック()不当なロック:それが来ると、最初に捕捉しますが、取得せずに、キューに入ります
//默认非公平锁,true表示公平锁,false表示非公平锁,
ReentrantLock reentrantLock = new ReentrantLock(false);
reentrantLock.lock();
//NonfairSync extends Sync 使用NonfairSync中的lock
public void lock() {
sync.lock();
}
final void lock() {
//采用cas设置state为1
if (compareAndSetState(0, 1))
//设置成功,则表示获得锁,并将当前线程设置为锁的拥有者
setExclusiveOwnerThread(Thread.currentThread());
else
//否则
acquire(1);
}
public final void acquire(int arg) {
//// 如果获取不到锁,就放进等待队列(addWaiter),然后阻塞直到成功获取到锁
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
int c = getState();
//如果标志State为0
if (c == 0) {
//当前等待队列为空,并进行CAS操作成功
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//则将当前线程设置为独占锁持有者
setExclusiveOwnerThread(current);
return true;
}
}
//如果当前锁的拥有者是当前线程
else if (current == getExclusiveOwnerThread()) {
//则将state加1,表示再次加锁,释放锁时候,只有当state值为0,才表示释放锁完成
int nextc = c + acquires;
//当nextc小于0,则表示超过了,锁的最大计数器
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//设置state为nextc,并返回true
setState(nextc);
return true;
}
//否则返回false
return false;
}
}
// 从clh中选一个线程获取占用资源
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 当节点的先驱是head的时候,就可以尝试获取占用资源了tryAcquire
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
// 如果获取到资源,则将当前节点设置为头节点head
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 如果获取失败的话,判断是否可以休息,可以的话就进入waiting状态,直到被unpark()
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private Node addWaiter(Node mode) {
// 封装当前线程和模式为新的节点,并将其加入到队列中
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
// tail为null,说明还没初始化,此时需进行初始化工作
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 否则的话,将当前线程节点作为tail节点加入到CLH中去
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
ロック()フェアロック フェアロック:古いスレッドがキューに入れられ、新しいスレッドもプリエンプトされずにキューに入れられます
ReentrantLock reentrantLock = new ReentrantLock(true);
reentrantLock.lock();
public void lock() {
sync.lock();
}
//FairSync extends Sync,使用FairSync中的lock()方法
final void lock() {
acquire(1);
}
public final void acquire(int arg) {
//// 如果获取不到锁,就放进等待队列(addWaiter),然后阻塞直到成功获取到锁
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
int c = getState();
//如果标志State为0
if (c == 0) {
//当前等待队列为空,并进行CAS操作成功
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//则将当前线程设置为独占锁持有者
setExclusiveOwnerThread(current);
return true;
}
}
//如果当前锁的拥有者是当前线程
else if (current == getExclusiveOwnerThread()) {
//则将state加1,表示再次加锁,释放锁时候,只有当state值为0,才表示释放锁完成
int nextc = c + acquires;
//当nextc小于0,则表示超过了,锁的最大计数器
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//设置state为nextc,并返回true
setState(nextc);
return true;
}
//否则返回false
return false;
}
}
// 从clh中选一个线程获取占用资源
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 当节点的先驱是head的时候,就可以尝试获取占用资源了tryAcquire
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
// 如果获取到资源,则将当前节点设置为头节点head
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 如果获取失败的话,判断是否可以休息,可以的话就进入waiting状态,直到被unpark()
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private Node addWaiter(Node mode) {
// 封装当前线程和模式为新的节点,并将其加入到队列中
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
// tail为null,说明还没初始化,此时需进行初始化工作
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 否则的话,将当前线程节点作为tail节点加入到CLH中去
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
CountDownLatchクラス
public class CountDownLatch {
/**
* 基于AQS的内部Sync
* 使用AQS的state来表示计数count.
*/
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
// 使用AQS的getState()方法设置状态
setState(count);
}
int getCount() {
// 使用AQS的getState()方法获取状态
return getState();
}
// 覆盖在共享模式下尝试获取锁
protected int tryAcquireShared(int acquires) {
// 这里用状态state是否为0来表示是否成功,为0的时候可以获取到返回1,否则不可以返回-1
return (getState() == 0) ? 1 : -1;
}
// 覆盖在共享模式下尝试释放锁
protected boolean tryReleaseShared(int releases) {
// 在for循环中Decrement count直至成功;
// 当状态值即count为0的时候,返回false表示 signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
private final Sync sync;
// 使用给定计数值构造CountDownLatch
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
// 让当前线程阻塞直到计数count变为0,或者线程被中断
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// 阻塞当前线程,除非count变为0或者等待了timeout的时间。当count变为0时,返回true
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
// count递减
public void countDown() {
sync.releaseShared(1);
}
// 获取当前count值
public long getCount() {
return sync.getCount();
}
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
}