从ReentrantLock出发看AQS(一)

AQS

什么是AQS?

是阻塞式锁和相关同步器工具的框架,如ReentrantLock内部就持有这个同步器,具体实现都是调用同步器的API实现加锁解锁

==========================ReentrantLock中的Sync================================
	private final ReentrantLock.Sync sync;

    public ReentrantLock() {//默认非公平
        this.sync = new ReentrantLock.NonfairSync();
    }
==========================Sync继承自AQS========================================
	abstract static class Sync extends AbstractQueuedSynchronizer

AQS特点

在这里插入图片描述
AQS采用模板模式:tryXXX方法是需要子类需要实现的,而Acquire是AQS提供的,调用的是tryXXX

//获取锁
public final void acquire(int arg) {
        if (!this.tryAcquire(arg) && this.acquireQueued(this.addWaiter(AbstractQueuedSynchronizer.Node.EXCLUSIVE), arg)) {
            selfInterrupt();
        }

    }
    
 //AQS只抛出异常
protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

AQS中的组件

主要有三个组件:State,ConditionObject,CHL队列

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements Serializable {
	//CHL队列
	private transient volatile AbstractQueuedSynchronizer.Node head;
    private transient volatile AbstractQueuedSynchronizer.Node tail;
    //资源状态位
    private volatile int state;
    static final long SPIN_FOR_TIMEOUT_THRESHOLD = 1000L;
	//条件变量
	public class ConditionObject implements Condition, Serializable {
	        private static final long serialVersionUID = 1173984872572414699L;
	        private transient AbstractQueuedSynchronizer.Node firstWaiter;
	        private transient AbstractQueuedSynchronizer.Node lastWaiter;
	        private static final int REINTERRUPT = 1;
	        private static final int THROW_IE = -1;
	
		}
//............
}

state:共享资源状态
ConditionObject:条件变量,把对资源争用的线程划分为不同种类,可根据条件对特定一组线程唤醒,条件变量上的等待队列是单链表

public static void main(String[] args) throws InterruptedException {

        ReentrantLock reentrantLock = new ReentrantLock();
        Condition condition1 = reentrantLock.newCondition();
        Condition condition2 = reentrantLock.newCondition();
        condition1.await();
        condition2.await();
    }

CHL队列:CHL队列是双向链表,在AQS中直接等待的队列,AQS中用head,tail指向头尾。

public static void main(String[] args) throws InterruptedException {

        ReentrantLock reentrantLock = new ReentrantLock();
        reentrantLock.wait();
    }
Node

上面说的等待队列中的节点是把线程做了一层封装,先看一下Node中重要的成员变量

static final class Node {
        static final AbstractQueuedSynchronizer.Node SHARED = new AbstractQueuedSynchronizer.Node();
        static final AbstractQueuedSynchronizer.Node EXCLUSIVE = null;
        //waitStatus描述,暂时不讨论其含义
        static final int CANCELLED = 1;
        static final int SIGNAL = -1;
        static final int CONDITION = -2;
        static final int PROPAGATE = -3;
        volatile int waitStatus;
        //前驱与后继节点
        volatile AbstractQueuedSynchronizer.Node prev;
        volatile AbstractQueuedSynchronizer.Node next;
        //节点中包含的线程
        volatile Thread thread;
        //下一个等待的节点
        AbstractQueuedSynchronizer.Node nextWaiter;
        //...........
}

知道了上面的基础知识就可以手动实现一个不可重入锁了

自定义锁

class myLock implements Lock {

    //独占锁,同步器内部类
    class MySync extends AbstractQueuedSynchronizer {
        @Override//加锁
        protected boolean tryAcquire(int arg) {
            //state变量加了volatile
            if (compareAndSetState(0,1)){//设置当前线程
                setExclusiveOwnerThread(Thread.currentThread());
            }
            return true;
        }

        @Override//解锁
        protected boolean tryRelease(int arg) {
            setExclusiveOwnerThread(null);
            //由于是独占锁,setState无需同步处理。但是必须在上一行之后,
            //因为volatile保证前面的操作能被其他线程读取,所以先把持有锁的线程设置为null
            setState(0);
            return true;
        }

        @Override//是否持有独占锁
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        public Condition newCondition(){
            return new ConditionObject();
        }
    }

    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 l, TimeUnit timeUnit) throws InterruptedException {
        return sync.tryAcquireNanos(1,timeUnit.toNanos(l));
    }

    @Override//解锁
    public void unlock() {
        sync.release(1);
    }

    @Override//创建条件变量
    public Condition newCondition() {
        return sync.newCondition();
    }
}

发现没有,实现一个简易的自制锁是如此简单,因为AQS底层已经封装好了必要的方法,后面说的ReentrantLock也是在此模板上进行扩展

ReentrantLock

特点

在这里插入图片描述
不可打断模式:在未获得锁的时候被打断仅会加上打断标记,在未获得锁的过程中对打断置之不理,继续驻留在队列。获得锁后会继续运行,只是打断标记变为true

可打断模式:尝试获得锁的过程中被打断就不会进入获取资源的for循环,会终止获得锁的行为,直接抛出异常

try {
    //如果获取失败进入阻塞队列,这时可以被其他线程interrupt打断
    reentrantLock.lockInterruptibly();
} catch (InterruptedException e) {
    //打断后操作
    e.printStackTrace();
}

非公平锁:不检查AQS队列,直接加锁。默认不公平,公平锁会降低并发度,非必要不使用

公平锁:先检查AQS队列,如果队列中没有第二或者第二节点不是当前线程则不会去竞争锁

ReentrantLock获取锁后执行的代码在try块中执行, Lock比Synchronize更广泛,且需要手动加锁解锁。发生异常时要在finally中释放锁,否则会造成死锁。

特定唤醒

为了便于理解ReentrantLock相对于Synchronize的特性,我们来实现一个特定唤醒的例子
通过标志位flag和condition来做到指定唤醒某个线程

class Share{

    private Lock lock = new ReentrantLock();
    private Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();
    private int flag = 1;

    public void a1() throws InterruptedException {
        lock.lock();
        try {
            while (flag != 1){
                c1.await();
            }
            System.out.println("do a1........");
            flag = 2;
            //c1执行完唤醒c2里的线程
            c2.signal();
        }finally {
            lock.unlock();
        }
    }

    public void a2() throws InterruptedException {
        lock.lock();
        try {
            while (flag != 2){
                c2.await();
            }
            System.out.println("do a2........");
            flag = 1;
            //c2执行完唤醒c1里的线程
            c1.signal();
        }finally {
            lock.unlock();
        }
    }
}

本篇总结

此篇文章初步了解了AQS和ReentrantLock的概念及特性,下一篇我们会深入了解它们之间底层的实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值