1 概述
队列同步器,全称是 AbstractQueuedSynchronizer,是用来构建锁或者其他同步组件的基础框架
特点:
- 用 state 属性来表示资源的状态(分独占模式和共享模式),子类需要定义如何维护这个状态,控制如何获取锁和释放锁
- 提供了FIFO的等待队列,类似于 Monitor 的EntryList
- 条件变量来实现等待、唤醒机制,支持多个条件变量,类似于 Monitor 的WaitSet
子类主要实现这样一些方法(默认抛出 UnsupportedOperationException)
方法名称 | 描述 |
---|---|
tryAcquire | 独占锁获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进行CAS设置同步状态 |
tryRelease | 独占式释放同步状态,等待获取同步状态的线程将有机会获取同步状态 |
tryAcquireShared | 共享式获取同步状态,返回大于等于0的值,表示获取成功,反之,获取失败 |
tryRelease | 共享式释放同步状态 |
isHeldExclusively | 当前同步器是否在独占模式下被线程占用,一般该方法表示是否被当前线程所独占 |
获取锁的姿势
//如果获取锁失败
if(!tryAcquire(arg)){
//入队,可以选择阻塞当前线程 park unpark
}
释放锁的姿势
if(tryRelease(arg)){
//让阻塞线程恢复运行
}
2 实现不可重入锁
2.1 自定义同步器
//独占锁 同步器类
class MySync extends AbstractQueuedSynchronizer{
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0,1)){
//加上了锁,并设置 owner 为当前线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
@Override
//是否持有独占锁
protected boolean isHeldExclusively() {
return getState() == 1;
}
public Condition newCondition(){
return new ConditionObject();
}
}
2.2 自定义锁
class MyLock implements Lock{
private MySync sync = new MySync();
@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();
}
}
2.3 测试
public static void main(String[] args) {
MyLock lock = new MyLock();
new Thread(() -> {
lock.lock();
try{
System.out.println("["+Thread.currentThread().getName()+"]"+"locking....");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printstacktrace();
} finally {
System.out.println("["+Thread.currentThread().getName()+"]"+"unlocking....");
lock.unlock();
}
},"t1").start();
new Thread(() -> {
lock.lock();
try{
System.out.println("["+Thread.currentThread().getName()+"]"+"locking....");
}finally {
System.out.println("["+Thread.currentThread().getName()+"]"+"unlocking....");
lock.unlock();
}
},"t2").start();
}
22:29:28.727 c.TestAqs [t1] - locking...
22:29:29.732 c.TestAqs [t1] - unlocking...
22:29:29.732 c.TestAqs [t2] - locking...
22:29:29.732 c.TestAqs [t2] - unlocking...
不可重入测试
如果改为下面代码,会发现自己也会被挡住(只会打印一次 locking)
lock.lock();
log.debug("locking...");
lock.lock();
log.debug("locking...");