AQS框架图
上图是AQS框架大致的一些方法,还有一些没有全部的列出来。这里主要是让大家从这张图中,对AQS有一个总体的印象。总的来说AQS框架分为五层从对外暴露的API层到底层的数据层,当自定义同步器时候,只需要实现API的方法即可,不需要关注底层的具体实现逻辑。
共享模式和独占模式流程图对比
独占模式流程图
共享模式流程图
相同点
从流程图中,我们发现独占模式和共享模式,在竞争失败时,都会调用addWaiter方法,此方法都会把当前线程封装为Node节点添加到队列中。
判断条件都是前驱节点是头节点的时候,设置当前节点为头节点,并且释放前驱节点。
不同点
共享模式比独占模式多做了一步操作,就是调用了doReleaseShared方法,去唤醒队列中所有共享模式的节点,让这些线程在去争夺共享资源,而独占式是没有这个操作的。
自定义锁
AQS核心就是控制state值,通过这个state标志来控制线程争抢锁的。所以我们通过AQS实现自定义锁,只需要在AQS上层提供的API里面写自己的业务逻辑,控制state值即可。下面我们通过AQS来写一个重入锁的代码,所谓重入锁就是指在当前线程占有锁后,如果进来的线程和当前占有锁的线程相同,那么仍然可以获取锁.
public class ZLock {
private static class Sync extends AbstractQueuedSynchronizer{
@Override
protected boolean tryAcquire(int arg) {
//1 第一个线程进来,设置为宿主线程直接返回true
//2 第二个线程进来,判断进来的线程和当前线程是否相同,
//如果相同可以获取到锁。否则获取不到锁,返回false.
int state=getState();
Thread t=Thread.currentThread();
if(state==0){
if(compareAndSetState(0,arg)){
setExclusiveOwnerThread(t);
return true;
}
}
else if(t==getExclusiveOwnerThread()){
setState(state+1);//记录同一个线程进来的次数
return true;
}
return false;
}
//锁的释放和加锁肯定是一一对应的,加锁了几次,就释放锁几次。
// 直到记录的状态值更新为0
@Override
protected boolean tryRelease(int arg) {
//释放锁时候,如果释放的线程不是当前的线程直接抛出异常
if(Thread.currentThread()!=getExclusiveOwnerThread()){
throw new RuntimeException();
}
int state=getState()-arg;
setState(state);
if(state==0){
setExclusiveOwnerThread(null);
return true;
}
return false;
}
}
private Sync sync=new Sync();
public void lock(){
sync.acquire(1);
}
public void unLock(){
sync.release(1);
}
}
public class Test {
static int value=0;
static ZLock myLock=new ZLock();
public int getValue() {
myLock.lock();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
value ++;
myLock.unLock();
return value;
}
public void a() {
myLock.lock();
System.out.println("a");
b();
myLock.unLock();
}
public void b() {
myLock.lock();
System.out.println("b");
myLock.unLock();
}
public static void main(String[] args) throws InterruptedException {
Test testMyLock = new Test();
//检测是否会并发
Runnable runnable = () -> {
for (int i = 0; i<100; i++) {
System.out.println(Thread.currentThread().getId()+",值:" + testMyLock.getValue());
}
};
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(runnable).start();
//检测重入锁
Runnable runnable1 = () -> testMyLock.a();
new Thread(runnable1).start();
}
}
测试结果
value值最终为300 说明锁生效
输出 a b 说明同一个线程锁可以重入
更多内容并发内容可以订阅