AQS(AbstractQueuedSynchronizer)应用案例-02
1.概述
通过对AQS源码的熟悉,我们可以通过实现AQS实现自定义的锁来加深认识。
2.实现
1.首先我们确定目标是实现一个独占模式的锁,当其中一个线程获得资源时,其他线程再来请求,让它进入队列进行公平的等待。于是,我们用 Sync 代表子类,来实现 AbstractQueuedSynchronizer
其中 tryAcquire 是们在尝试获取资源时,通过子类来判断是否成功的方法,这里需要我们自己实现(父类有默认的实现方法,是因为不需要每个子类都同时实现共享和非共享模式时获取资源的方法)。
因为是独占锁,我们便用 state = 0 为锁无人使用, state = 1 为 锁被占用,arg参数用来表示当前获取资源的个数,独占锁只能获取一个,便先校验是否为1,接着通过CAS尝试改变state的值为1,如果成功则返回true,失败返回 false
@Override protected boolean tryAcquire(int arg) { assert arg == 1; boolean success = false; if (compareAndSetState(0,1)){ setExclusiveOwnerThread(Thread.currentThread()); success = true; } return success; }
同样的,释放锁时,我们也只能释放一把锁,因为释放锁不会出现竞争,因此直接将state设为0即可。 setExclusiveOwnerThread 是一个设置当前持有锁的线程的引用,当释放锁时,需要将持有锁的当前线程设置为null
@Override protected boolean tryRelease(int arg) { assert arg == 1; if(arg == 0){ throw new IllegalMonitorStateException(); } setState(0); setExclusiveOwnerThread(null); return true; }
isHeldExclusively 用来判断是否是当前线程持有锁,这里可以进行一个 == 判断即可
@Override protected boolean isHeldExclusively() { return Thread.currentThread() == getExclusiveOwnerThread(); }
3.完整源码如下
同步锁工具类,静态内部类Sync 实现 AbstractQueuedSynchronizer
public class CusLock { public CusLock() { sync = new Sync(); } public void lock(){ sync.lock(); } public void unlock(){ sync.unlock(); } private final Sync sync; static class Sync extends AbstractQueuedSynchronizer{ void lock(){ if (compareAndSetState(0,1)){ setExclusiveOwnerThread(Thread.currentThread()); }else { acquire(1); } } void unlock(){ release(1); } @Override protected boolean tryAcquire(int arg) { assert arg == 1; boolean success = false; if (compareAndSetState(0,1)){ setExclusiveOwnerThread(Thread.currentThread()); success = true; } return success; } @Override protected boolean tryRelease(int arg) { assert arg == 1; if(arg == 0){ throw new IllegalMonitorStateException(); } setState(0); setExclusiveOwnerThread(null); return true; } @Override protected boolean isHeldExclusively() { return Thread.currentThread() == getExclusiveOwnerThread(); } } }
1. SynOrder 中 getOrder()方法中用lock.lock() 来占有锁,
2. 打印线程相关信息
3. 睡眠5秒
4. 释放锁
public class SynOrder { private CusLock lock = new CusLock(); public void getOrderNo() { try { lock.lock(); System.out.println(Thread.currentThread().getName() + "--" + SimpleDateFormat.getTimeInstance(SimpleDateFormat.FULL).format(new Date())); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } finally { lock.unlock(); } } }
1.通过开启2个线程来对synOrder中的getOrderNo产生竞争,导致阻塞。可以清楚的见到实际上 用AQS来实现自定义的锁,在已有的concurrent包下已够用
public class Main { public static void main(String[] args) throws InterruptedException { SynOrder synOrder = new SynOrder(); Thread t1 = new Thread(new Runnable() { @Override public void run() { synOrder.getOrderNo(); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { synOrder.getOrderNo(); } }); System.out.println("t1:"+t1.getName()); System.out.println("t2:"+t2.getName()); t1.start(); t2.start(); Thread.currentThread().join(); } }