• 工作中需要用到的Java知识(AQS篇)


    AQS是在面试的时候比较常问的内容,那么今天我们就来简单了解一下什么是AQS。

    什么是AQS?

    我们来简单说说什么是AQS,AQS其实是指Java中的AbstractQueuedSynchronizer类,这个类在java.util.concurrent.locks包下,是Java用来实现轻量级锁的类。

    AQS的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

    CLH队列是根据发明者三人名字的首字母命名的。CLH队列从逻辑上形成一个锁等待队列(实际无队列实例,仅通过节点间的关联来实现)从而实现加锁,CLH锁只支持按顺序加锁和解锁(FIFO先入先出),不支持重入,不支持中断

    AQS中还有一个state变量,用来判断当前是否为同步状态(通过CAS方法来改变state的值),0:资源空闲,当state变量大于0时表示资源正在锁定中,且state的值表示锁定资源的重入次数。

    AQS的工作流程就是基于CLH虚拟队列,用volatile关键字修饰共享变量state,想要获取锁的线程通过CAS方法去改变state变量,成功则获取到锁,失败则进入等待队列中等待唤醒

    AQS采用了模板模式的设计方法

    我们只需要按照模板创建自己的类,就可以生成一个自定义的同步器(锁)。

    首先我们要遵守这两步:

    1. 内部类同步器需要继承AbstractQueuedSynchronizer并重写指定的方法。

    2. 外部类同步器需要实现Lock接口与Serializable接口提供对外服务。

    我们去创建自定义同步器的时候只需要实现共享变量state的获取与释放即可,等待队列的维护都由AQS底层实现。

    自定义同步器需要实现的方法:

    方法名作用
    isHeldExclusively()该线程是否正在独占资源。只有用到condition才需要去实现它
    tryAcquire(int)独占方式。尝试获取资源,成功则返回true,失败则返回false。
    tryRelease(int)独占方式。尝试释放资源,成功则返回true,失败则返回false。
    tryAcquireShared(int)共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
    tryReleaseShared(int)共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

    ReentrantLock为例,(可重入独占式锁):state变量初始化为0,表示未锁定状态,A线程lock()时,会调用tryAcquire()独占锁并将state+1.之后其他线程再想tryAcquire的时候就会失败,直到A线程unlock()到state=0为止,其他线程才有机会获取该锁。A释放锁之前可以重复获取此锁(state累加),这就是可重入的概念。
    注意:获取多少次锁就要释放多少次锁,保证state是能回到零态的。

    CountDownLatch为例,任务分N个子线程去执行,state就初始化为N,N个线程并行执行,每个线程执行完之后countDown()一次,state就会CAS减一。当N子线程全部执行完毕,state=0,会unpark()主调用线程,主调用线程就会从await()函数返回,继续之后的动作。

    接下来我们尝试自定义实现一个同步器。

    1. /**
    2. * @description: 自定义同步器
    3. * @author: Me
    4. * @createDate: 2022/11/21 11:09
    5. * @version: 1.0
    6. */
    7. public class NonReentrantLock implements Lock, Serializable {
    8. // 同步器实现时一般都将自定义同步器定义为内部类,供自己使用,并实现某个接口,向外提供服务,一般实现Lock接口
    9. // 内部类的好处在于可以变相实现多继承
    10. // 内部类会对同一个包下的其他类隐藏,内部类可以使用private关键字修饰
    11. // 内部类可以访问外部类的全部字段与方法,外部类访问内部类需要创建内部类对象,内部类中不能有静态成员
    12. private static class Sync extends AbstractQueuedSynchronizer{
    13. @Override
    14. protected boolean tryAcquire(int arg) {
    15. assert arg == 1;
    16. // cas方法,将变量0改为1
    17. if (compareAndSetState(0,1)){
    18. // 修改成功则上锁
    19. setExclusiveOwnerThread(Thread.currentThread());
    20. return true;
    21. }
    22. return false;
    23. }
    24. @Override
    25. protected boolean tryRelease(int arg) {
    26. assert arg == 1;
    27. //如果state为0,则抛出异常
    28. if (getState()==0){
    29. throw new IllegalMonitorStateException();
    30. }
    31. // 如果state变量不为0,则释放锁
    32. setExclusiveOwnerThread(null);
    33. setState(0);
    34. return true;
    35. }
    36. @Override
    37. protected boolean isHeldExclusively() {
    38. // 是否锁已经被持有
    39. return getState() == 1;
    40. }
    41. //提供条件变量接口
    42. public Condition newCondition(){
    43. return new ConditionObject();
    44. }
    45. }
    46. Sync sync = new Sync();
    47. @Override
    48. public void lock() {
    49. sync.acquire(1);
    50. }
    51. @Override
    52. public void lockInterruptibly() throws InterruptedException {
    53. sync.acquireInterruptibly(1);
    54. }
    55. @Override
    56. public boolean tryLock() {
    57. return sync.tryAcquire(1);
    58. }
    59. @Override
    60. public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
    61. return sync.tryAcquireNanos(1, unit.toNanos(time));
    62. }
    63. @Override
    64. public void unlock() {
    65. sync.release(1);
    66. }
    67. @Override
    68. public Condition newCondition() {
    69. return sync.newCondition();
    70. }
    71. }

    到这里AQS就简单介绍完啦,希望对小伙伴们有所帮助。

  • 相关阅读:
    springboot227旅游管理系统
    flannel网络
    VS 配置 OpenCV (亲测可用)
    【HTML】重点知识内容~快速上手
    2023.10.20期中考核复现(misc)
    [附源码]java毕业设计社区医院管理系统
    最小堆提升每次排序的效率
    js异步任务的简单总结
    [排序算法] 快速排序 (C++) (含三种写法)
    java遇到的问题02
  • 原文地址:https://blog.csdn.net/weixin_49290171/article/details/127959283