java 佇列同步器AbstractQueuedSynchronizer(AQS)
阿新 • • 發佈:2018-11-09
佇列同步器AbstractQueuedSynchronizer(AQS),是用來構建鎖或者其他同步元件的基礎框架如對獲取鎖失敗執行緒的阻塞、喚醒,都是AQS替我們實現,ReentrantLock,ReentrantReadWriteLock和CountDownLatch等都是用AQS實現的。
佇列同步器AbstractQueuedSynchronizer:
- 內部使用一個int變數state表示同步狀態。
- 內部使用一個隱式的FIFO佇列(並沒有宣告這樣一個佇列,只是通過每個節點記錄它的上下節點來從邏輯上產生一個佇列)來完成阻塞執行緒的排隊。
- 同步器提供的3個方法getState()、setState(int newState)和compareAndSetState(int expect,int update)來進行狀態操作(三個操作是安全的)。
- 支援獨佔鎖和共享鎖(讀所和寫鎖)
- 同步器的方法:
1:可重寫的方法
方法名稱 |
描述 |
boolean tryAcquire(int arg) |
獨佔式獲取同步狀態,成功返回true,失敗返回false。 |
boolean tryRelease(int arg) |
獨佔式釋放同步狀態,成功返回true,失敗返回false。 |
int tryAcquireShared(int arg) |
共享式獲取同步狀態,獲取成功則返回值>=0 |
boolean tryReleaseShared(int arg) |
共享式釋放同步狀態,成功返回true,失敗返回false。 |
boolean isHeldExclusively() |
判斷同步器是否在獨佔模式下被佔用,一般用來表示同步器是否被當前執行緒佔用
|
2:設定AQS的同步狀態state的方法
方法 |
描述 |
int getState() |
獲取當前同步狀態 |
void setState(int newState) |
設定當前同步狀態 |
boolean compareAndSetState(int expect, int update) |
使用CAS設定當前狀態,保證狀態更新的原子性 |
3:AQS 提供的模板方法
自定義一個簡單的獨佔鎖
-
繼承lock介面
-
構建一個靜態內部類繼承AQS
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
class TestLock implements Lock {
// 靜態內部類,自定義同步器
private static class Sync extends AbstractQueuedSynchronizer {
// 當狀態為 0 的時候獲取鎖
public boolean tryAcquire(int acquires) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 釋放鎖,將狀態設定為0
protected boolean tryRelease(int releases) {
// 如果當前執行緒沒有獲得鎖也就是 getState不為1 直接丟擲異常
if (getState() == 0) throw new
IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
// 返回一個Condition,每個condition都包含了一個condition佇列
Condition newCondition() {
return new ConditionObject();
}
}
// 僅需要將操作代理到Sync上即可
private final Sync sync = new Sync();
// 獲取鎖
@Override
public void lock() {
sync.acquire(1);
}
// 嘗試獲取鎖
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public void unlock() {
sync.release(1);
}
// 可中斷的獲取鎖
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
// 嘗試獲取鎖
@Override
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public Condition newCondition() {
return sync.newCondition();
}
}
可以看到我們利用 AQS 構建一個鎖是非常簡單的 執行緒的阻塞 狀態切換等 都給我們提供了相應方法我們只需要重寫幾個方法就可以了。