ReentrantLock源码阅读

一、  ReentrantLock概念

ReentrantLock,是java并发编程中提供的一种高级同步机制。实现了lock接口和Serializable接口,基于AQS(AbstractQueuedSynchronizer)实现了锁的功能。是一种可重入的互斥锁,支持公平锁和非公平锁的选择。

二、  相关属性

ReentrantLock 的实现是基于内部的sync 的属性实现,而sync 继承了AbstractQueuedSynchronizer,其核心属性有以下部分。

        1、  private volatile int state:线程获取锁时候需要将state从0改为1,state为0代表没有线程持有锁,state大于0 代表某个线程正持有这把锁,state的值代表重入次数。

        2、  private transient volatile Node head: 同步列表的头部指向下一个线程封装成的Node对象        

        3、  private transient volatile Node tail: 同步列表尾部元素,后期竞争失败的元素的prev指向tail,tail赋值为新的node.

三、构造函数

        1、  无参构造函数 生成非公平锁

public ReentrantLock() {
        sync = new NonfairSync();
    }

        2、有参构造函数,根据传入值 生成公平锁或非公平锁

 public ReentrantLock(boolean fair) {
         //传入参数为true为公平锁 false 非公平锁
        sync = fair ? new FairSync() : new NonfairSync();
    }

        3、构造函数中的两个内部类(FairSync与NonFairSync)都继承了Sync,两个主要区别的两个方法lock()和tryAcquire() (两个的tryAcquire的逻辑基本是一致的)
        FairSync的 lock()

final void lock() {
            //直接进入 acquire
          acquire(1);
        }

        NonFairSync的Lock

final void lock() {
            //直接尝试修改state获取锁
            //获取成功设置锁持有线程为当前线程
            //获取失败进入 acquire
            if (compareAndSetState(0, 1))            setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        Sync的 acquire()

 public final void acquire(int arg) {
        //tryAcquire 成功直接返回
        //
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
protected final boolean tryAcquire(int acquires) {
            //获取当前线程和state的值
            final Thread current = Thread.currentThread();
            int c = getState();
            // state 为0 去竞争锁,设置锁拥有线程
            if (c == 0) {
                // hasQueuedPredecessors 这个是公平锁特有的
                //判断当前线程是否需要排队,也就是同步队列是否存在线程排队,存在则需要排队,不存在,不需要排队直接尝试获取锁。
                // 非公平锁直接竞争锁
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // state不为0 判断当前线程是否为锁拥有线程
            // 是 锁重入
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
四、  常见方法

1、  lock方法 获取

 public void lock() {
        sync.lock();
    }
 public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                //判断node的前一个元素是否是head
                //如果是去获取锁,获取成功设置head为当前node
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //判断线程是否被挂起进入等待
                if (shouldParkAfterFailedAcquire(p, node) &&
                    //挂起当前线程并检查线程是否被中断
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                // 取消等待
                cancelAcquire(node);
        }
    }
   private Node addWaiter(Node mode) {
       //将线程封装成Node
        Node node = new Node(Thread.currentThread(), mode);
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            //将新生成Node放在同步队列末尾
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }      2、  tryLock 获取 
        tryLock 的源码说明它只会获取一次锁,获取成功返回true,获取失败直接返回false,不会进去等待队列
public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
      3、  tryLock(Long timeOut ,TimeUnit unit)获取超时失败,不进入等待队列。
      4、  unLock() 释放锁,并唤醒等待队列后续线程
public void unlock() {
        sync.release(1);
    }
public final boolean release(int arg) {
        //释放锁
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                //唤醒后续线程
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
protected final boolean tryRelease(int releases) {
            // 重入次数减少
            int c = getState() - releases;
            //判断线程是否拥有锁 不拥有用抛出异常
            if (Thread.currentThread() !=         getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                //锁释放成功
                free = true;
                //设置锁拥有线程为null
                setExclusiveOwnerThread(null);
            }
            //修改state值
            setState(c);
            return free;
        }
      5、  getHoldCount 获取当前线程保持此锁的次数。

6、  isHeldCurrentThread 判断当前线程是否是持有锁的线程。

7、  isLocked 锁是否已经被获取,判断state是否为0。

8、  getOwner 获取持有锁的线程。

五、  特点

1、  可重入:正如其名称,ReentrantLock 是一个可重入的锁,意味着持有锁的线程可以 多次获取该锁,而不会引发阻塞。

2、  手动释放:与synchronized 不同,ReentrantLock 需要手动调用lock或unlock 获取释放锁,更加灵活也更加容易忘记释放锁。

3、  公平锁与非公平锁

a)  公平锁按线程请求顺序获取锁,先到先得

b)  非公平锁:不保证锁的获取顺序,线程请求时候会先去竞争一次锁,新能可能更好。

4、  trylock():可以非阻塞模式下使用,使用一些场景

a)  非阻塞操作‌:当线程不需要等待锁释放时,可以使用tryLock方法。如果锁不可用,线程可以立即执行其他任务,而不是进入等待状态。

b)  资源竞争较小‌:在资源竞争较小的情况下,使用tryLock可以减少线程的等待时间,提高程序的响应速度。

c)  避免死锁‌:在某些情况下,使用tryLock可以避免因长时间等待锁而导致的死锁问题。

六、  使用注意事项

1、  必须手动释放锁:忘记释放锁会导致其他线程永远无法获取锁。因此,通常在 try 块中获取锁,在 finally 块中释放锁,以确保异常情况下锁也能被正确释放。

2、  避免死锁:当多个线程获取锁时候,容易出现死锁问题。为了避免这问题,可以采用锁超时机制,或者设计更合理的锁加载顺序。

3、  新能与公平性的平衡:非公平锁性能较好,但可能导致某些线程长时间无法获取锁。公平锁避免了线程锁竞争的问题,但每次加锁和解锁性能较差。

4、  避免锁竞争:过度使用锁可能导致线程竞争性能下降。在设计多线程应用时,尽量减少临界区的长度,避免不必要的锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值