ReentrantLock介绍和代码演示和实现原理

介绍

互斥锁
实现Lock接口
并且最好在 finally 块中释放

公平锁
非公平锁 如果已经进入队列,链表里面的线程是先进先出,如果已经释放了锁,在抢占锁时,链表里面的头结点和还没有入队列的线程抢锁


使用代码


public class Test {
    public static void main(String[] args) {
       Task task = new Task();
       for(int i = 0; i < 10; i++) {
          new Thread(task).start();
       }
    }
}

class Task implements Runnable {
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        lock.lock();
        try {
            Thread.sleep(1000);
            System.out.println("业务代码");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

实现原理

采用AQS+CAS+LockSupport用来阻塞和唤醒线程)
  ReentrantLock有三类内部类,实现都在其内部类Sync中,默认是使用非公平锁NonFairSync ,非公平锁可提高效率,在可重入锁时可以减少线程切换开销。可以通过构造方法切换公平和非公平

Sync父类AbstractQueuedSynchronize(AQS)
此处的锁具备synchronized功能,即可以阻塞一个线程。为了实现一把具有阻塞或唤醒功能的锁,需要几个核心要素

(1) state变量,标记锁状态。至少有两个值0/1。对state的操作,使用CAS保证线程安全
AbstractQueuedSynchronizer类里有变量private volatile int state 记录锁状态
state=0,没有线程持有锁,exclusiveOwnerThread=null
state=1,有一个线程持有锁,exclusiveOwnerThread=该线程
state > 1,说明该线程重入了该锁,等于几就重入了几次
(2) 需要记录当前是哪个线程持有锁
AbstractOwnableSynchronizer里面有变量private transient Thread exclusiveOwnerThread;记录锁持有的线程
(3) 需要底层支持对一个线程进行阻塞或唤醒操作
public native void unpark(Object thread); // 唤醒某一个线程
public native void park(boolean isAbsolute, long time); // 阻塞某一个线程 实现一个线程对另外一个线程的精准唤醒
一般使用LockSupport的工具类,对上面两个方法进行了封装
当前线程中调用park()就会被阻塞 另一个线程调用unpark(Thread t)传入被阻塞线程就可唤醒阻塞在park()地方的线程
(4) 需要有一个队列维护所有阻塞的线程。这个队列也必须是线程安全的无锁队列,也需要使用CAS对队列进行增加或删除

public abstract class AbstractQueuedSynchronizer {
  // 双向链表
  static final class Node {
  volatile Thread thread;  // 每个Node对应一个被阻塞的线程
  volatile Node prev;      // 前一个
  volatile Node next;      // 后一个
 }
private transient volatile Node head;   // 头
private transient volatile Node tail;   // 尾

}
  阻塞队列是整个AQS核心中的核心。如下图所示,head指向双向链表头部,tail指向双向链表尾部。入队就是把新的Node加到tail后面,然后对tail进行CAS操作;出队就是对head进行CAS操作,把head向后移一个位置

在这里插入图片描述

初始的时候,head=tail=NULL;然后,在往队列中加入阻塞的线程时,会新建一个空的Node,让head和tail都指向这个空Node;之后,在后面加入被阻塞的线程对象。所以,当head=tail的时候,说明队列为空。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值