大家好,这里是淇妙小屋,一个分享技术,分享生存的博主
以下是我的主页,各个主页同步更新优质博客,创作不易,还请大家点波关注
掘金主页
知乎主页
Segmentfault主页
开源中国主页
后续会公布更多MySQL,Redis,并发,JVM,分布式等面试热点常识,以及Java学习路线,面试重点,职业规划,面经等相干博客
转载请表明出处!

0. Lock与AQS类图

1. Lock接口

1.1 Lock的定义

Lock接口定义了锁的API操作,用于实现java中锁机制

public interface Lock {    //如果锁可用,则取得锁后返回    //如果锁不可用,那么以后线程会阻塞,直到获取锁后才会返回    void lock();        //可中断的获取锁(取得锁的过程可中断)    //如果锁可用,则取得锁后返回    //如果锁不可用,那么线程会阻塞获取锁,阻塞获取锁的过程是可中断的,受到中断会抛出InterruptedException    void lockInterruptibly() throws InterruptedException;        //非阻塞的尝试取得锁    //如果锁可用,取得锁后,返回true    //如果锁不可用,返回false    boolean tryLock();    //尝试获取锁(取得锁的过程可中断)    //如果锁可用,取得锁后,返回true    //如果锁不可用,那么在指定工夫内会一直的尝试取得锁    //如果胜利取得锁——返回true    //如果被中断——抛出InterruptedException    //如果超时——返回false    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;    //开释锁    void unlock();    //获取Condition对象,Condition对象与以后Lock对象绑定,以后线程只有取得了锁    //能力调用该Condition对象的await()办法,而调用后,以后线程将开释锁,进入Conditon对象的期待队列中WATING    //当其余线程调用Condition对象的signal(),才会唤醒Condition对象的期待队列中的WATING线程    Condition newCondition();}

1.2 Lock与sychronized的异同

  • 共同点

    • Lock的子类中有可重入锁
  • 区别

    • Lock显式的获取锁与开释锁

      sychronized隐式的获取锁与开释锁

    • Lock只能给代码块上锁

      sychronized能够给代码块,办法上锁

    • Lock依赖JDK实现

      sychronized依赖JVM实现

    • Lock有独占模式与共享模式,每个模式还分偏心锁与非偏心锁

      sychronized是独占模式的非偏心锁

    • Lock是可中断的,线程在取得锁的过程中是能够影响中断

      sychronized不可中断,线程在阻塞期待锁的开释的时候,是不会响应中断的

    • Lock能够设定超时工夫,超时会返回

      sychronized不行

2. Condition接口

2.1 Condition接口定义

//Condition对象——1.相当于一个期待队列  2.必须与一个锁实例绑定  3.一个锁实例能够有多个Condition对象//相当于monitor对象的WaitSet//必须持有Condition对象绑定的锁,才能够调用其办法public interface Condition {    //相当于Object.wait(),可中断    //持有锁的线程开释锁,进入期待队列中,线程状态更变为WATING,直到以下2种状况醒来    //1.有其余线程调用期待队列的signal()或signalAll()    //2.被其余线程中断    //醒来后的线程会从期待队列挪动到锁实例的同步队列,状态由WAITING更改为BLOKING    void await() throws InterruptedException;    //同await(),然而不可中断    void awaitUninterruptibly();    //同 await(),然而多了一种唤醒状况——期待超时(单位纳秒)    long awaitNanos(long nanosTimeout) throws InterruptedException;    //同 await(),然而多了一种唤醒状况——期待超时    boolean await(long time, TimeUnit unit) throws InterruptedException;       //同 await(),然而多了一种唤醒状况——期待超时    boolean awaitUntil(Date deadline) throws InterruptedException;    //将期待队列中的首节点挪动到锁实例的同步队列,而后通过LockSupport唤醒线程    void signal();    //将期待队列中的全副节点挪动到锁实例的同步队列,而后通过LockSupport唤醒线程    void signalAll();}

2.2 Condition与monitor的区别

  • Condition——期待队列中期待的线程是可响应中断的

    monitor——期待队列中期待的线程不可响应中断

  • Condition————期待队列中的线程能够指定期待到将来的某个具体工夫点

    monitor——不反对

3. AQS

3.1 AQS介绍

AbstractQueuedSynchronizer形象队列同步器——用于构建锁或其余同步组件的根底框架

子类通过继承AQS并实现它的形象办法来实现锁

AQS反对两种模式——独占模式,共享模式

3.2 AQS构造

AOS的外围字段——exclusiveOwnerThread

  • 独占模式下,持有锁的线程

AQS最外围字段——state

  • state用于示意同步器的状态(可称为同步状态)
  • AQS的不同子类对state的使用不同

    • 对于ReentrantLock

      • state=0——示意同步器没有被占用(没有线程持有锁)

        state!=0——示意同步器已被占用(有线程正在应用锁)

      • 尝试获取锁——查看state的值是否为0,如果为0尝试用CAS批改其值,如果不为0—则未获得锁

        尝试开释锁——将state的值CAS批改为0

    • 对于CountDownLatch,CyclicBarrier

      • state有不同的使用

Node的状态——waitStatus

  • CANCELLED(1):同步队列中的节点被中断或者超时
  • INITIAL(0):初始化状态
  • SIGNAL(-1)
  • CONDITION(-2):示意节点在期待队列中
  • PROPAGATE(-3):

AQS有5个办法供子类锁去实现

  • 独占模式

    • boolean tryAcquire()
    • boolean tryRelease()
    • boolean IsHeldExclusively()
  • 共享模式

    • boolean tryAcquireShared()
    • boolean tryReleaseShared()

3.3 AQS独占模式

AQS独占模式下

  • 同步队列的首节点持有锁
  • 如果线程尝试获取锁失败,那么会封装成一个Node,而后退出同步队列,并开始自旋
  • 节点从同步队列移除的条件——前继节点为首节点 and 胜利取得锁
  • 节点开释锁时——会唤醒其后继节点

3.3.1 获取锁

同步队列中,只有当节点的前继节点是首节点,能力尝试获得锁,起因如下

  1. 首节点是获得锁的节点,首节点开释锁后,会唤醒其后继节点

    后继节点被唤醒后须要查看本人的前继节点是否为首节点

  2. 保护同步队列的FIFO准则

3.3.2 开释锁

3.3.3 超时获取锁

过程根本与获取锁雷同

区别在于

  1. 每次自旋操作,在判断本人是否须要被阻塞之前,会优先判断是否已超时,如果超时了,就返回false
  2. 如果须要被阻塞,还会查看残余的工夫有没有大于阈值

    如果大于阈值——通过LockSupport让线程进行超时阻塞

    LockSupport.parkNanos(this, nanosTimeout);

3.4 AQS共享模式

3.4.1 共享模式与独占模式的区别

共享模式与独占模式的区别在于——同一时刻是否有多个线程能够同时获取到锁

  • 共享模式拜访资源——其余共享式拜访容许,独占式拜访不容许
  • 独占模式拜访资源——其余拜访一律不容许,被阻塞

3.4.2 共享模式取得锁

3.4.3 共享模式开释锁

4. ReentrantLock实现AQS的独占模式

4.1 ReentrantLock介绍

可重入:任意线程取得锁后可能再次获取该锁而不会被锁阻塞

ReentrantLock实现了AQS的独占模式,是一个可重入锁,还分为 偏心锁非偏心锁

  • 偏心锁:先对锁进行获取申请的线程肯定先取得锁
  • 非偏心锁

非偏心锁的效率高于偏心锁

非偏心锁可能呈现 线程饥饿问题——局部线程迟迟无奈取得资源

ReentrantLock大多数办法的实现都是Sync及其子类来实现,ReentrantLock只是对外裸露了接口

4.2 ReentrantLock取得锁

4.2.1 非偏心锁

4.2.2 偏心锁

4.2.3 偏心锁与非偏心锁的不同

FairSync和NonfairSync的 lock() 和 tryAcquire() 逻辑不同

  1. 非偏心锁在lock()办法的开始就会尝试去通过CAS批改同步状态以取得锁,偏心锁不会
  2. 在自旋时,非偏心锁和偏心锁都会在前继节点为同步队列首节点时调用tryAcquire()尝试获取锁

    tryAcquire()中,如果state为0,那么非偏心锁不会关怀节点在同步队列中的地位,间接尝试CAS批改state取得锁;然而非偏心锁关怀节点的地位,会查看是否有前继节点,如果有,就会放弃

上述2点保障了偏心锁肯定是——先对锁进行获取申请的线程肯定先取得锁,而非偏心锁不肯定

4.3 ReentrantLock开释锁

偏心锁开释锁与非偏心锁开释锁采纳同一个逻辑