一、定义
AQS全称是:AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架。框架可理解为所有工具类均要继承/实现该父类才可使用。
特点:
用state属性来表示资源的状态(独占模式或共享模式),子类需要定义如何维护此状态,控制如何获取锁和释放锁;
- getState:获取 state 状态;<br /> - setState:设置 state 状态;<br /> - compareAndSetState :cas机制设置 state 状态。注意AQS本身是阻塞式锁,只是设置state状态时采用了cas,防止多线程同时设置state,保证线程安全;<br /> - 独占模式是只有一个线程能够访问资源,而共享模式可以允许多个线程访问资源;
提供了基于FIFO的等待队列,类似Monitor的EntryList;
- 使用条件变量来实现等待、唤醒机制,支持多个条件变量,类似于Monitor的WaitSet;
子类主要实现这样一些方法(默认抛出UnsupportedOperationException)
- tryAcquire
- tryRelease
- tryAcquireShared
- tryReleaseShared
- isHeldExclusively
获取锁的姿势(模板):
//如果获取锁失败
if(!tryAcquire(arg)){
//入队,可以选择阻塞当前线程
}
释放锁的姿势(模板):
//如果释放锁成功
if(tryRelease(arg)){
//让阻塞线程恢复运行
}
二、应用
使用AQS实现自定义锁(不可重入锁)
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class TestAQS {
}
class MyLock implements Lock{
//创建内部类,使用AQS创建锁
class MySync extends AbstractQueuedSynchronizer{
@Override
protected boolean tryAcquire(int arg) {
//cas保证操作原子性,根本上解决了线程安全问题
if(compareAndSetState(0,1)){
//加锁成功,设置锁的owner为当前线程
setExclusiveOwnerThread(Thread.currentThread());
}
}
@Override
protected boolean tryRelease(int arg) {
//取消锁的持有者,设置持有锁的线程==null
setExclusiveOwnerThread(null);
//设置状态为0
setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {
//注意这里是在判断独占锁是否被持有,而不是在判断独占锁是否被当前线程持有
return getState()==1;
}
protected Condition newCondition(){
return new ConditionObject();
}
}
MySync mySync = new MySync();
@Override //加锁,不成功会进入等待队列
public void lock() {
//这里调用了AQS中自带的方法,acquire是当无法获取锁时,线程进入阻塞队列等待锁,tryAcquire方法是获取不到锁则不再获取,只一次;
mySync.acquire(1);
}
@Override //加锁,可打断
public void lockInterruptibly() throws InterruptedException {
mySync.acquireSharedInterruptibly(1);
}
@Override //尝试加锁(一次)
public boolean tryLock() {
return false;
}
@Override //尝试加锁(带超时)
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return mySync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override //解锁
public void unlock() {
mySync.release(1);
/*
* release在释放锁后会唤醒阻塞队列中等待锁的线程
* 而tryRelease在释放锁会并不会唤醒线程
* */
}
@Override //创建条件变量
public Condition newCondition() {
return mySync.newCondition();
}
}
本例中,AQS的使用较为清晰。AQS可理解为一种将锁包装起来的工具类,以往线程获取锁需要先创建某一对象,然后在对象上加锁再获取锁。AQS相当于创建获得锁、释放锁等接口,将实现细节(如synchronized语句块)封装,只暴露接口给外部,以供调用。这个思想和ReentrantLock类似,ReentrantLock的实现是调用lock()与unlock()思想,也体现了封装的思想。
类中state属性用来表示锁是否被持有。一般state=0表示锁未被持有,state>0表示锁被持有;
AQS是一种阻塞式锁(悲观锁),这是由其内部实现的。具体表现为AQS的acquire方法:尝试获取锁,当无法获取锁时则会进入阻塞队列等待锁释放。
与ReenTrantLock使用方法类似,线程首先加锁、在try中实现需要加锁的代码,并在finally块中解锁;
本例中的自定义锁还是一种不可重入锁,因为lock方法调用AQS的tryAcquire方法,而tryAcquire方法的实现细节即为:cas试图将state从0改为1,可以防止锁重入
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class TestAQS {
public static void main(String[] args) {
MyLock myLock = new MyLock();
new Thread(()->{
myLock.lock();
try {
System.out.println("locking...");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
myLock.unlock();
}
},"t1").start();
new Thread(()->{
myLock.lock();
try {
System.out.println("locking...");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
myLock.unlock();
}
},"t2").start();
}
}
class MyLock implements Lock{
//创建内部类,使用AQS创建锁
class MySync extends AbstractQueuedSynchronizer{
@Override
protected boolean tryAcquire(int arg) {
//cas保证操作原子性,根本上解决了线程安全问题
if(compareAndSetState(0,1)){
//加锁成功,设置锁的owner为当前线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
//取消锁的持有者,设置持有锁的线程==null
setExclusiveOwnerThread(null);
//设置状态为0
setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {
//注意这里是在判断独占锁是否被持有,而不是在判断独占锁是否被当前线程持有
return getState()==1;
}
protected Condition newCondition(){
return new ConditionObject();
}
}
MySync mySync = new MySync();
@Override //加锁,不成功会进入等待队列
public void lock() {
//这里调用了AQS中自带的方法,acquire是当无法获取锁时,线程进入阻塞队列等待锁,tryAcquire方法是获取不到锁则不再获取,只一次;
mySync.acquire(1);
}
@Override //加锁,可打断
public void lockInterruptibly() throws InterruptedException {
mySync.acquireSharedInterruptibly(1);
}
@Override //尝试加锁(一次)
public boolean tryLock() {
return false;
}
@Override //尝试加锁(带超时)
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return mySync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override //解锁
public void unlock() {
mySync.release(1);
/*
* release在释放锁后会唤醒阻塞队列中等待锁的线程
* 而tryRelease在释放锁会并不会唤醒线程
* */
}
@Override //创建条件变量
public Condition newCondition() {
return mySync.newCondition();
}
}