基於AQS實現自定義鎖

AQS

全稱:AbstractQueuedSynchronizer,譯爲:抽象隊列同步器。

AQS是很多併發工具類的基礎,可以說是實現整個java.util.concurrent併發包的半壁江山。

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable

AbstractQueuedSynchronizer是一個抽象類,本身不能實例化。

它用到了“模板方法設計模式”,定義了一個模板骨架,子類需要實現部分流程方法才能使用。

重要方法

  • int getState()
    獲取同步狀態。

  • void setState()
    設置同步狀態。

  • boolean compareAndSetState(int expect, int update)
    基於CAS操作來設置同步狀態。

setState和compareAndSetState

這兩個方法的區別是:一個是非原子的,一個是原子的。

什麼時候用setState?

釋放鎖時使用setState,因爲只有獲取鎖的線程才能調用該方法,不存在併發問題,所以無需使用CAS操作。

什麼時候用compareAndSetState?

加鎖時使用compareAndSetState,因爲鎖競爭時是併發的,設置同步狀態操作必須是CAS的,否則可能多個線程同時加鎖成功。

子類重寫方法

AQS的功能可以分爲兩類:獨佔式與共享式

獨佔式:同一時刻最多隻允許一個線程獲得鎖,
共享式:同一時刻允許多個線程獲得鎖,

獨佔式

  • boolean tryAcquire(int arg)
    獨佔式嘗試獲取鎖。

  • boolean tryRelease(int arg)
    獨佔式嘗試釋放鎖。

  • boolean isHeldExclusively()
    判斷當前線程是否獲取獨佔鎖。

共享式

  • boolean tryAcquireShared(int arg)
    共享式嘗試獲取鎖。

  • boolean tryReleaseShared(int arg)
    共享式嘗試釋放鎖。

根據自己要實現的鎖類型,重寫對應的方法即可。

模板方法

AQS定義了一組模板方法,子類不允許重寫,可直接調用。

獨佔式

  • void acquire(int arg)
    獨佔式獲取鎖。

  • void acquireInterruptibly(int arg)
    獨佔式獲取鎖,支持中斷。

  • boolean tryAcquireNanos(int arg,long nanos)
    獨佔式嘗試獲取鎖,支持超時。

  • boolean release(int arg)
    獨佔式釋放鎖。

共享式

  • void acquireShared(int arg)
    共享式獲取鎖。

  • void acquireSharedInterruptibly(int arg)
    共享式獲取鎖,支持中斷。

  • boolean tryAcquireSharedNanos(int arg,long nanos)
    共享式嘗試獲取鎖,支持超時。

  • boolean releaseShared(int arg)
    共享式釋放鎖。

自定義鎖實例

MyLock
/**
 * @Author: 潘
 * @Date: 2019/11/23 16:18
 * @Description: 基於Lock和AQS實現自定義鎖
 */
public class MyLock implements Lock {
	//AQS實例
	private final Sync sync = new Sync();

	/**
	 * 繼承AQS 實現獨佔鎖
	 * state 0無鎖  1有鎖
	 */
	static class Sync extends AbstractQueuedSynchronizer{
		/**
		 * 重寫tryAcquire
		 * 獲取鎖成功和失敗均輸出提示文字
		 * @param arg
		 * @return
		 */
		@Override
		protected boolean tryAcquire(int arg) {
			if (compareAndSetState(0, arg)) {
				//設置獨佔鎖線程爲當前線程
				setExclusiveOwnerThread(Thread.currentThread());
				System.out.println(Thread.currentThread().getName()+"鎖競爭成功");
				return true;
			}
			System.out.println(Thread.currentThread().getName()+"鎖競爭失敗");
			return false;
		}

		//嘗試釋放鎖
		@Override
		protected boolean tryRelease(int arg) {
			setState(arg);
			setExclusiveOwnerThread(null);
			return true;
		}

		@Override
		protected boolean isHeldExclusively() {
			return super.isHeldExclusively();
		}
	}

	@Override
	public void lock() {
		sync.acquire(1);
	}

	@Override
	public void lockInterruptibly() throws InterruptedException {
		sync.acquireInterruptibly(1);
	}

	@Override
	public boolean tryLock() {
		return sync.tryAcquire(1);
	}

	@Override
	public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
		return sync.tryAcquireNanos(1, unit.toNanos(time));
	}

	@Override
	public void unlock() {
		sync.release(0);
	}

	@Override
	public Condition newCondition() {
		return sync.new ConditionObject();
	}
}

測試

class MyLockDemo{
	private MyLock lock = new MyLock();

	void test(){
		lock.lock();
		SleepUtil.sleep(1000);
		System.out.println(Thread.currentThread().getName());
		lock.unlock();
	}

	public static void main(String[] args) {
		MyLockDemo demo = new MyLockDemo();
		for (int i = 0; i < 3; i++) {
			new Thread(()->{
				demo.test();
			}).start();
		}
	}
}

輸出如下:

Thread-0鎖競爭成功
Thread-2鎖競爭失敗
Thread-1鎖競爭失敗
Thread-2鎖競爭失敗
Thread-2鎖競爭失敗
Thread-0
Thread-2鎖競爭成功
Thread-2
Thread-1鎖競爭成功
Thread-1
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章