基本原理
AQS名为队列同步器,内部就是通过维护了一个FIFO队列来完成获取资源线程的排队工作,即如果同时有多个线程想获取共享资源,等待的线程将会进入此队列。AQS无法确定共享的资源到底是什么,但是可以需要维护共享资源的同步状态。实际上,AQS内部用一个int变量state来代表同步状态, 用volatile修饰来保证线程可见性。
private volatile int state;
用户可以自己去实现如何表示同步状态。比如,如果一个线程成功获取了资源,那么state的值就加1;那么其它线程想获取资源时,看见这个同步状态state的值为1,便知道已经有一个线程获取了资源。相应的,如果一个线程释放了资源,state的值就减1,这样其它线程便知道了这个资源现在可以争抢使用了。
上面是如何实现同步状态的一个例子。state状态信息可以通过getState, setState和compareAndSetState方法进行重写和操作。
AQS支持两种资源的同步方式: 独占式(Exclusive) 和 共享式(Shared)。 独占式就是只能由一个线程去执行访问资源(如 ReentrantLock), 共享式则允许多个线程同时执行(如 Semaphore/CountDownLatch)。这样方便使用者实现不同类型的同步组件,可以自由发挥。
实现
代码比较简单,就是仿照ReentrantLock,创建一个内部类继承了AQS,并重写了tryAcquire和tryRelease方法。具体解释在代码注释。
package com.cute.leetcode.editor.cn.test;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* @author yuxuefei
* @date 2022/3/24 10:40
*/
public class MyLock implements Lock
{
private LockSync sync = new LockSync();
static class LockSync extends AbstractQueuedSynchronizer
{
@Override
protected boolean tryAcquire(int arg)
{
//aqs维护的volatile共享变量state值
int c = getState();
//cas修改state值0->1,(简单解释一下,cas保证0->1这个操作的原子性,所以只有一个线程能够修改成功, 因为当一个线程修改成功后,另这个内存地址就会变为1,另一个线程去进行cas操作时发现这个旧值已经是1而不是期望值0了,cas操作就会失败。) 如果成功,获得锁
if (c == 0 && compareAndSetState(0, arg))
{
//设置独占锁为当前线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg)
{
//设置当前没有线程获得锁
setExclusiveOwnerThread(null);
//恢复state为0。此处无需cas操作,因为只是单纯的重置state为0
setState(0);
return true;
}
public Condition newCondition()
{
return new ConditionObject();
}
}
@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(1);
}
@Override
public Condition newCondition()
{
return sync.newCondition();
}
public static void main(String[] args)
{
MyLock myLock = new MyLock();
new Thread(() ->{
try
{
myLock.lock();
System.out.println(Thread.currentThread().getName() + "获取到锁");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
myLock.unlock();
}
},"t1").start();
new Thread(() ->{
myLock.lock();
System.out.println(Thread.currentThread().getName() + "获取到锁");
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
myLock.unlock();
}, "t2").start();
}
}