AQS & ReentrantLock & CountDownLatch

14 篇文章 0 订阅

主要参考文章
https://javadoop.com/2017/06/16/AbstractQueuedSynchronizer/

首先介绍AQS基本原理,然后介绍AQS在ReentrantLock和CountDownLatch上的应用

AQS

概述

AQS全程是AbstractQueuedSynchronizer,顾名思义是一个基于队列的同步器。底层使用LockSupport.park和LockSupport.unPark实现对线程的挂起和唤醒。

在这里插入图片描述

如果使用了condition,情况就会稍微有些复杂
以下面代码为例

package com.test.CountDownLatchTest.AQS;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 创建两对线程,每一对线程分为wait和signal两部分,并使用各自的condition进行线程同步
 * 2021-07-25
 */
public class testAQS {
    public static final Lock lock = new ReentrantLock();
    public static final Condition condition1 = lock.newCondition();
    public static final Condition condition2 = lock.newCondition();
    public static void main(String[] args) throws Exception {
        Thread thread1_wait = new Thread(() -> {
            lock.lock();
            try {
                System.out.println("thread1_wait begin");
                condition1.await();
                System.out.println("thread1_wait end");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "thread1_wait");
        Thread thread2_wait = new Thread(() -> {
            lock.lock();
            try {
                System.out.println("thread2_wait begin");
                condition2.await();
                System.out.println("thread2_wait end");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "thread2_wait");

        Thread thread1_signal = new Thread(() -> {
            lock.lock();
            try {
                System.out.println("thread1_signal begin");
                Thread.sleep(10000);
                condition1.signal();
                System.out.println("thread1_signal end");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();// 断点1
            }
        }, "thread1_signal");

        Thread thread2_signal = new Thread(() -> {
            lock.lock();
            try {
                System.out.println("thread2_signal begin");
                Thread.sleep(1000);
                condition2.signal();
                System.out.println("thread2_signal end");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();//断点2
            }
        }, "thread2_signal");

        thread2_wait.start();
        Thread.sleep(100);
        thread1_wait.start();
        Thread.sleep(100);
        thread1_signal.start();
        Thread.sleep(100);
        thread2_signal.start();
    }
}
//AQS中的函数
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

首先理清一下四个线程的时序图

在这里插入图片描述
每一个condition有一个队列,用于存储调用condition.await()的线程。当其他线程调用condition.signal()时,AQS会讲node从condition队列中移动到阻塞队列中
在这里插入图片描述

可以通过添加断点证明上述时序图的正确性。下面举几个例子
“8 运行结束,释放锁”,在"断点1"处添加断点。可以看到阻塞队列中有三个节点,第一个是头节点,第二个是thread2_signal,第三个是thread1_wait。证明了第5步和第7步的顺序问题。
在这里插入图片描述
“12 运行结束,释放锁”,在"断点2"处添加断点。可以看到阻塞队列中有三个节点,第一个是头节点,第二个是thread1_wait,第三个是thread2_wait。证明了第7步和第11步的顺序问题。

在这里插入图片描述

由于程序中存在Thread.sleep函数,建议每次只测试一个断点

源码

待续。可参考文章开始处的参考链接。

ReentrantLock

待续。AQS的排它锁

CountDownLatch

待续。AQS的共享锁

其他

ReentrantLock 公平与非公平

在非公平的状态下,新来的线程在入队之前会尝试抢一次锁,如果失败了就会乖乖进入队列,一旦进入队列是不能再次出来抢的,只能等待队列一个一个地执行完毕(进入到了队列中的线程就公平了,先进入队列的一定比后进入的先获取到锁)。所谓不公平是指新来的线程会不会在入队之前尝试「野蛮」地抢锁,公平的时候是不会,但是非公平的时候是会的。

中断

此文章最后一节

参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值