ReentrantLock深度剖析(公平锁,非公平锁)

ReentrantLock深度剖析(公平锁,非公平锁)

它类似于Synchronized互斥锁,可以保证线程安全。基于AQS强⼤的并发特性和处理多线程的能⼒,ReentrantLock相⽐Synchronized,拥有更多的特性,⽐ 如⽀持⼿动加锁、解锁,⽀持公平锁等。

ReentrantLock默认使⽤⾮公平锁,也可以通过构造器来显⽰的指定使⽤公平锁。在ReentrantLock中还有两个类继承⾃Sync:

• NonfairSync (非公平锁)

• FairSync (公平锁)

1.1公平锁

reentrantLock一个非常重要的属性sync,由于reentrantlock没有直接继承AbstractQueuedSynchronizer 类,而是由它的属性sync继承了AbstractQueuedSynchronizer 类。判断是否能够上锁的state属性就是来自AbstractQueuedSynchronizer,exclusiveOwnerThread属性也来自AbstractQueuedSynchronizer,exclusiveOwnerThread是用来存放当前持有锁的线程。
线程1进来尝试去拿锁,先去判断属性state(被volatile修饰,默认是0,修改它的值,其他线程能立马嗅探到并重新获得其值)是否等于0,等于0就拿到锁,并将state设置为1.其他线程进来尝试去拿锁,也同样判断state的值是否为0,不是0则进入等待队列(双向列表“CLH”)。
当线程1释放锁时,state变成0,刚进来的线程尝试去拿锁,通过判断hasQueuePredecessors(通过判断队列head+1是否等于尾部来判断队列中是否有线程在等待)是否为false来决定能否拿到锁,只有当队列中没有其他线程在等待时,该线程才能拿到锁,否则要进入队列尾部进行等待。

1.2非公平锁

非公平锁在拿到state=0后,直接尝试去上锁,多个线程都尝试通过CAS的SetState将state设置为1,当线程2设置成功后,将当前线程赋值到exclusiveOwnerThread属性,没成功的线程则进入队列。当线程2再次尝试拿锁,先判断exclusiveOwnerThread属性是否等于线程2,由于是同一线程则将state值+1(可重入锁)。

1.3线程抢占失败后

其他线程抢占失败后则尝试进入队列

private Node addWaiter(Node mode){
    Node node = new Node(Thread.currentThread(), mode); 
   // Try the fast path of enq; backup to full enq on failure
   	Node pred = tail;
      if (pred != null) { 
           node.prev = pred; 
              if (compareAndSetTail(pred, node)) {
                  pred.next = node; return node; 
              } 
      }
    enq(node); 
    return node; 
}

第一次循环会通过CAS将队列的head(头部),tall(尾部)设置为null,将新创建的node放在head,第二次循环将带有线程(首次进入等待队列的线程)的node的prev指向空node并且将空node的next指向带有线程的node,此时队列的head指向空的node,tall指向带有线程的node。

非首次进入等待队列的线程,存放进node中并将node的prev指向队列的tall,队列tall的next指向node,完成后队列的tall指向了新进来的node。

进入队列的线程要进行阻塞,在阻塞之前队列头部的next会在抢一次锁,如果抢锁成功,头节点出队列,否则进入阻塞。这些阻塞的线程在lock.unlock()中被唤醒。

猜你喜欢

转载自blog.csdn.net/ILIKETANGBOHU/article/details/127120007