• ReentrantLock和AQS源码解析


    建议去B站先看下子路老师的视频

    ReentrantLock源码

    • synchronized通过在对象头的markword进行操作从而实现互斥锁
    • ReentrantLock通过将线程加入AQS阻塞队列从而实现同步互斥锁

    首先初始化一个ReentrantLock

    ReentrantLock lock = new ReentrantLock(true);
    

    这个时候是默认的构造函数,新建一个公平锁

    public ReentrantLock(boolean fair){
        sync = fair ? new FairSync() : new NonfairSync();
    }
    

    然后加锁操作lock.lock(),这个方法会去调用FairSync下面的lock()方法

    final void lock(){
        acquire(1);
    }
    

    接着我们进入这个acquire(1)去看看,原来它是AQS的一个方法

    public final void acquire(int arg) {
    	if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    

    首先第一个判断是tryAcquire(arg),我们应该去它的子类,也就是Sync去看,这个时候也就是去FairSync类的tryAcquire(arg)去看

    protected final boolean tryAcquire(int acquires) {
    	final Thread current = Thread.currentThread();
    	int c = getState();
    	if (c == 0) {
            if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
             }
    	}
        else if (current == getExclusiveOwnerThread()) {
        	int nextc = c + acquires;
            if (nextc < 0)
            	throw new Error("Maximum lock count exceeded");
    		setState(nextc);
            return true;
    	}
    	return false;
    }
    

    AQS源码(自旋+CAS+park/unpark)

    AQS(AbstractQueuedSynchronizer)类的设计主要构成

    // 队首
    private transient volatile Node head;
    // 队尾
    private transient volatile Node tail;
    // 锁状态
    private volatile int state;
    // 持有锁的那个线程(这个属性其实是在AbstractOwnableSynchronizer类里的)
    private transient Thread exclusiveOwnerThread;
    

    AQS中的入队操作源码解析

    addWaiter
    private Node addWaiter(Node mode) {
    	Node node = new Node(Thread.currentThread(), mode);
    	// Try the fast path of enq; backup to full enq on failure
    	Node pred = tail;
    	if (pred != null) {
    		node.prev = pred;
            // 直接入队,然后返回
            if (compareAndSetTail(pred, node)) {
            	pred.next = node;
                return node;
            }
        }
        // enq使第一个线程入队
    	enq(node);
        return node;
    }
    
    enq
    // 通过这个函数我们可以看出AQS中的head一直都是指向一个空的Node节点
    private Node enq(final Node node) {
        // 若当前队列为空,那么for循环会执行两次,第一次执行if(将head指向一个空的Node),第二次执行else(将当前的node连接到上个空Node之后,也就是入队)
    	for (;;) {
    		Node t = tail;
            // 第一次会执行if(将head指向一个空的Node)
    		if (t == null) { // Must initialize
    			if (compareAndSetHead(new Node()))
    				tail = head;
    		}
            // 第二次会执行else(将当前的node连接到上个空Node之后,也就是入队)
            else {         
                // 连接到上一个空Node,即入队
            	node.prev = t;
                // 将队列尾节点指向当前节点node
                if (compareAndSetTail(t, node)) {
                	t.next = node;
                	return t;
                }
    		}
    	}
    }
    

    入队后的队列结构如下图所示:注意,队列头为空节点,代表的是当前获得锁的线程。也可以理解为持有锁的线程永远不会在队列里(保证持有锁的线程不参与排队的原则),比如这个时候t1拿到锁了,那么t1会被置位head,并且里面的值全为null(见acquireQueued方法中的setHead方法)

    多线程非交替执行下。比如t1线程获取了ReentrantLock锁,这个时候t2线程来了,对t2的处理流程图如下图所示:

    总结

    ReentrantLocksynchronized快,是因为前者的加锁操作是在JDK的工作层次下操作的,而后者需要调用操作系统去工作。

    • ReentrantLock中一部分操作是在JDK的层面下解决的,还有一部分也是在操作系统中解决的;
      • 多线程交替执行(不会交叉,比如线程t1执行完了t2才开始执行,这个时候和队列没有关系):比如上面贴出的AQS中的acquire方法,首先是执行tryAcquire这个方法是不会调用操作系统的,如果这个方法返回true了,那就不会再继续判断后面调用操作系统的的acquireQueued方法
      • 多线程不是交替执行的:这个时候就会AQS中的队列就不为空了。

    synchronized在JDK1.6之前全都要经过操作系统来解决,所以那个时候提出了ReentrantLock

    ReentrantLock.lockTnterruptibly()

    if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                        throw new InterruptedException();
    
    if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                        interrupted = true;
    
  • 相关阅读:
    mui 时间选择器和上传图片
    css实现聊天气泡效果
    css总结 -使用display:inline-block,出现元素高度错位
    js DIV延时几秒后消失或显示代码
    js监听textarea 内容的变化,并计算内容的长度c
    生成git的SSH公钥
    IDEA中把普通的Java项目转换成gradle项目
    Idea中自动生成get,set,toString等方法
    IDEA中阿里P3C插件的安装与使用
    IDEA中SonarLint的安装与使用
  • 原文地址:https://www.cnblogs.com/flyingrun/p/13740443.html
Copyright © 2020-2023  润新知