AQS是多线程同步器, 它是JUC包包中多个组件的底层实现, 比如Lock,CountDownLatch,Semaphore到用到AQS. 从本质上来说, AQS提供一两种锁的机制, 分别是排它锁和共享锁. 所谓排它锁 存在多个线程去竞争同一共享资源的时候,同一时刻只允许一个线程去访问这样一个共享资源, 也就是说, 多个线程中,只能有一个线程, 去获得锁这样一个资源, 比如Lock中的ReentrantLock重入锁, 它的一个重现就是用到AQS中的一个排它锁的功能. 共享锁也称为读锁, 就是在同一个时刻允许多个线程同时获得这样一个锁的资源, 比如CountDownLatch以及Semaphore都用到了AQS中共享锁的功能, AQS作为互斥锁来说, 他的整个设计体系中, 需要解决三个核心的问题, 第一个互斥变量的设计, 互斥变量的设计, 以及如何保证多线程同时更新互斥变量的时候线程的安全性, 第二个, 未竞争到锁资源的线程的等待, 以及竞争到锁的资源释放锁之后的唤醒, 第三个锁竞争的公平性和非公平性,AQS采用了一个int类型的互斥变量state, 用来记录锁竞争的状态, 0表示当前没有任何线程竞争锁资源, 而>=1表示已经有线程正在持有锁资源. 一个线程来获取锁资源的时候, 首先会判断state是否等于0, 也就是说他是无锁状态, 如果是,则把这个state更新成1, 表示占用到锁, 而这个过程中, 如果多个线程同时去做这样一个操作, 就会导致线程安全性问题, 因此AQS采用了CAS机制, 去保证state变量更新的一个原子性; 未获得锁的线程通过Unsafe类中的park方法去进行阻塞, 把阻塞的线程按照先进先出的原则去加入到一个双向链表的一个结构中, 当获得锁资源的线程释放锁之后, 会从这样一个双向链表的头部支唤醒下一个等待的线程, 再去竞争锁, 最后关于锁竞争的公平性和非公平性的问题, AQS的处理方法是在竞争锁资源的时候, 公平锁需要去双向链表中是否有阻塞的线程, 如果有则需要去排队等待, 而非公平锁的处理方式是不管双向链表中是否存在等待竞争锁的线程, 那么他都会直接去尝试更改互斥变量state去竞争锁, 假设在一个临界点获得锁的线程释放锁, 此时state等于0, 而当前这个线程去抢占锁的时候, 刚好可以把state修改为1, 那么这个时候就表示他可以拿到锁, 而这个过程是非公平的.
面试-Spring: 谈谈你对AQS的理解
于 2022-04-22 11:59:50 首次发布