队列同步器AbstractQueuedSynchronizer(AQS)是实现各种锁的关键,因此要了解锁的原理或者自己实现锁必须先了解AQS。
API
获取和修改AQS状态的几个方法
- protected final int getState(): 获取同步状态
- protected final void setState(int newState):设置同步状态
- protected final boolean compareAndSetState(int expect, int update):原子的设置同步状态
第三个,原来的状态与expect相等,则修改为update状态,否则返回false
可以被重写的方法
- protected boolean tryAcquire(int arg): 独占式获取同步状态,在此方法中需要查询当前状态并判断是否符合预期,然后CAS设置同步状态
- protected boolean tryRelease(int arg): 独占式释放同步状态, 等待获取同步状态的线程有机会拿到同步状态
- protected int tryAcquireShared(int arg):共享式获取同步状态,返回>=0表示获取成功,否则表示获取失败
- protected boolean tryReleaseShared(int arg):共享式释放同步状态
- protected boolean isHeldExclusively():当前同步器是否在独占模式下被线程占用
AQS提供的模板方法
- public final void acquire(int arg): 独占式获取同步状态,获取成功则返回,否则进入同步队列等待,会调用重写的tryAcquire(int arg)
- public final void acquireInterruptibly(int arg):独占是获取同步状态,但是能响应中断,被中断抛异常
- public final boolean tryAcquireNanos(int arg, long nanosTimeout):独占是获取同步状态,能响应中断,且有超时,超时返回false
- public final void acquireShared(int arg):共享式获取同步状态,没获取到则进入同步队列,同一时刻可以有多个线程获取到同步状态
- public final void acquireSharedInterruptibly(int arg):可以被中断
- public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout):带超时,且能被中断
- public final boolean release(int arg):独占式释放同步状态,释放后会将同步队列第一个节点唤醒
- public final boolean releaseShared(int arg): 共享式释放同步状态;
- public final Collection getQueuedThreads(): 获取同步队列里的线程
一些说明:
- 1.可以把AQS理解为管理状态的一个东西,然后你可以通过重写他的一些方法定义自己的规则
- 2.共享式:指几个线程可以同时获得同步状态,独占式:指只能单独的线程获得同步状态
- 3.方法比较多,不要怕,通过下边的例子和之后对一些锁的实现原理分析就能理解了
AQS实现独占不可重入锁
LockTest内部结构
方法使用全部可以上面API部分找到
编写锁
同步器编写
锁的方法重写
测试
public static void main(String[] args) {
Lock lock = new NotReentrantLock();
//线程1
new Thread(() -> {
lock.lock();
try {
for (int i = 0; i < 30; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
Thread.sleep(2000);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
new Thread(() -> {
lock.lock();
try {
for (int i = 0; i < 30; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
}
} finally {
lock.unlock();
}
}).start();
}
复制代码