认识下ReentrantLock

  • ReentrantLock是可重入的互斥锁,虽然具有与synchronized相同功能,但是会比synchronized更加灵活(具有更多的方法
  • ReentrantLock底层基于AQS(AbstractQueuedSynchronizer)实现

    简单介绍下AQS

  • ReentrantLock实现的前提就是AbstractQueuedSynchronizer,简称AQS,是java.util.concurrent的核心,CountDownLatch、FutureTask、Semaphore、ReentrantLock等都有一个内部类是这个抽象类(AQS)的子类。

  • 整个AQS是典型的模板模式的应用,设计得十分精巧,对于FIFO队列的各种操作在AQS中已经实现了,AQS的子类一般只需要重写tryAcquire(int arg)和tryRelease(int arg)两个方法即可

    公平锁的思想

  • 所谓公平锁就是因为在AQS中存在一个先进先出的双向链表结构队列来存放线程节点,多个线程在这个结构中需要排队去取锁

  • 线程加锁的过程:

    1、当一个线程去加锁时会先尝试(tryAcquire)去加锁:
    1)加锁成功直接返回,当前线程就直接往下执行。
    当锁的状态不为0时(0代表锁没有被抢走)就会判断这把锁是不是在当前线程手里,如果是会触发
    锁重入机制。
    2)加锁失败后就会加入(addWaiter)等待队列
    3)第2步完成后,首先会判断当前线程的节点的上一个节点是否是head节点,如果是head节点,就会
    再次尝试获取锁,如果获取成功就会把当前线程设置为head结点(当前节点的属性thread和prev设 置为null); 如果不是head节点或是head节点但获取锁还是失败了,就会设置上一个节点的属性 (waitStatus, 属性值为0代表节点后面没有节点了,为-1代表后面还有节点)为-1,之后就会调用 LockSupport.park(this);这时候就阻塞住,要等待别的线程来唤醒

    非公平锁的思想

  • 所谓非公平是因为在AQS中存在一个先进先出的双向链表结构队列来存放线程节点,多个线程在这个结构中前两次不需要排队去取锁,当两次都失败后还是得乖乖地排队

  • 线程加锁的过程:

1、当一个线程去加锁时会先尝试(tryAcquire)去加锁:
1)加锁成功时直接返回,当前线程就直接往下执行,如果加锁失败后,再交尝试去加锁(注:这次加
锁没有像公平锁那样去判断队列里面有没有其他线程在排队),如果成功了就直接返回。
当锁的状态不为0时(0代表锁没有被抢走)就会判断这把锁是不是在当前线程手里,如果是会触发
锁重入机制。
2)如果上面两次都加锁失败了就会加入(addWaiter)等待队列
3)这步和公平锁的第3步一样的

解锁

  • 解锁中公平锁和非公平锁的操作是一样的
  • 解锁的操作过程:

1)获取锁的的状态值去减1,如果有锁重入要多次调用unpack,每调用一次减1,直到为止才算锁已经释放

2)锁释放了之后立马要把把独占锁字段(ExclusiveOwnerThread)设置为null
3)找到队列中的head节点的一个节点中的线程去unpack,至此这个线程就被唤醒了,这个线程就会去把自
己设置为head结点