ReentrantLock(重入锁)简单源码分析

1.ReentrantLock是基于AQS实现的一种重入锁。

2.先介绍下公平锁/非公平锁

公平锁

  • 公平锁是指多个线程按照申请锁的顺序来获取锁。

非公平锁

  • 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。

3.重入锁/不可重入锁

可重入锁:广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。

不可重入锁:不可重入锁,与可重入锁相反,不可递归调用,递归调用就发生死锁。

4.统一入口

 //获取锁。
    @Override
    public void lock() {
        sync.lock();
    }

    //获取锁,响应中断
    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    //仅在调用时锁未被另一个线程保持的情况下,才获取该锁。
    @Override
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

    //如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被 中断,则获取该锁。
    @Override
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    //试图释放此锁。
    @Override
    public void unlock() {
        sync.release(1);
    }

ReentrantLock默认是非公平模式,可以通过构造函数指定模式。

/**
     * 构造函数,默认非公平锁
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * 构造函数,通过布尔参数设置是否是公平锁
     * @param fair
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

4.非公平模式的加锁和解锁

加锁:

//获取锁。
    @Override
    public void lock() {
        sync.lock();
    }

然后调用NonfairSync的lock方法:

/**
         * 加锁
         */
        @Override
        final void lock() {
            //以cas方式尝试将AQS中的state从0更新为1
            if (compareAndSetState(0, 1)) {
                //获取锁成功则将当前线程标记为持有锁的线程,然后直接返回
                setExclusiveOwnerThread(Thread.currentThread());
            } else {
                acquire(1);
            }
        }

如果设置state失败,则调用NonfairSync的tryAcquire方法

@Override
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

然后调用Sync的nonfairTryAcquire方法:

/**
         * 非公平模式的情况下获取同步状态
         * @param acquires
         * @return
         */
        final boolean nonfairTryAcquire(int acquires) {
            //获取当前线程实例
            final Thread current = Thread.currentThread();
            //获取同步state变量的值,即当前锁被重入的次数
            int c = getState();
            //state为0,说明当前锁未被任何线程持有
            if (c == 0) {
                //以cas方式获取锁
                if (compareAndSetState(0, acquires)) {
                    //将当前线程标记为持有锁的线程
                    setExclusiveOwnerThread(current);
                    //获取锁成功,非重入
                    return true;
                }
                //当前线程就是持有锁的线程,说明该锁被重入了,实现重入
            } else if (current == getExclusiveOwnerThread()) {
                //计算state变量要更新的值
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                //非同步方式更新state值
                setState(nextc);
                //获取锁成功,重入
                return true;
            }
            //尝试获取锁失败
            return false;
        }

这边体现了锁的重入,用state的次数表示锁被线程重入了state次。

else if (current == getExclusiveOwnerThread()) {
                //计算state变量要更新的值
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                //非同步方式更新state值
                setState(nextc);
                //获取锁成功,重入
                return true;
            }

解锁

//试图释放此锁。
    @Override
    public void unlock() {
        sync.release(1);
    }

然后调用Sync的tryRelease方法:

/**
         * 释放同步状态
         * @param releases
         * @return
         */
        @Override
        protected final boolean tryRelease(int releases) {
            //计算待更新的state值
            int c = getState() - releases;
            //判断当前线程是否独占
            if (Thread.currentThread() != getExclusiveOwnerThread()) {
                throw new IllegalMonitorStateException();
            }
            //是否释放同步状态成功的标志
            boolean free = false;
            //待更新的state值为0,说明持有锁的线程未重入,一旦释放锁其他线程将能获取
            if (c == 0) {
                free = true;
                //清除锁的持有线程标记
                setExclusiveOwnerThread(null);
            }
            //更新state值
            setState(c);
            return free;
        }

锁的最终释放要去锁对于获取进行计数自增,计算表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数为0时表示已经释放成功。

5.公平模式的加锁和解锁

加锁:

@Override
        protected final boolean tryAcquire(int acquires) {
            //获取当前线程实例
            final Thread current = Thread.currentThread();
            //获取同步state变量的值,即当前锁被重入的次数
            int c = getState();
            //state为0,说明当前锁未被任何线程持有
            if (c == 0) {
                /**
                 * 1.判断同步队列中当前节点是否有前驱节点
                 * 2. 以cas方式获取锁
                 */
                if (!hasQueuedPredecessors() &&
                        compareAndSetState(0, acquires)) {
                    //将当前线程标记为持有锁的线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
                //当前线程就是持有锁的线程,说明该锁被重入了,实现重入
            } else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                //非同步方式更新state值
                setState(nextc);
                //获取锁成功,重入
                return true;
            }
            //尝试获取锁失败
            return false;
        }

对于非公平锁,只要cas设置同步状态成功,则表示当前线程获取了锁,而公平锁则不同,公平锁的获取增加了hasQueuedPredecessors的判断,即加入了判断同步队列中当前节点是否有前驱节点,

如果返回true,则表示有线程比当前线程更早滴请求获取锁,因此需要等待前驱线程获取并释放锁之后才能继续获取锁。

锁:同公平锁的解锁方式

 

 

原文地址:https://www.cnblogs.com/ql211lin/p/10439108.html

时间: 2024-08-26 15:06:07

ReentrantLock(重入锁)简单源码分析的相关文章

ReentrantLock (重入锁) 源码浅析

一.ReentrantLock简介ReentrantLock重入锁,顾名思义,就是支持重入的锁,它表示能够支持一个线程对资源的重复加锁:我们之前学习过Synchronized锁,它也是支持重入的一种锁,参考我的另一篇Synchronized 锁的实现原理与应用,Synchronized支持隐式的重入锁,比如递归方法,在方法运行时,执行线程在获取到了锁之后仍能连续多次地获取锁:ReentrantLock虽然不能隐式重入,但是获取到锁的线程多次调用lock方法,不会阻塞进入同步队列:除此之外在获取锁

ReentrantLock 重入锁

一.Lock接口: 在Java SE 5之后,并发包中新增了Lock接口及相关实现类来实现锁功能. Lock接口和synchronized关键字实现锁的区别: (1)Lock接口需要显示的获取和释放锁,sychronized是隐式的获取和释放锁.也正因为如此,使得Lock接口拥有了锁获取与释放的可操作性.可中断的获取锁.超时获取锁 等多种synchronized关键字所不具备的同步特性. 例如:一个场景,先获取锁A,然后获取锁B,当B获得后,同时释放A,同时获取锁C.以此类推.这种场景synch

ReentrantLock重入锁

上次博客讲到了通过wait()方法和notify()方法来实现循环打印数字和字母得问题.其实使用重入锁也可以实现同样得功能,那么开始我们先通过源码来了解一下重入锁把. public void lock() { sync.lock(); } 首先它有一个lock()方法,它用来加锁,从代码中可以看到,它调用得是sync.lock()方法, public class ReentrantLock implements Lock, java.io.Serializable { private stati

ReentrantLock 重入锁(下)

前沿: ReentrantLock 是java重入锁一种实现,在java中我们通常使用ReentrantLock 和 synchronized来实现锁功能,本篇通过例子来理解下ReentrantLock使用以及什么是可重入锁. 理解可重入: 1. 锁机制是为了多线程并发访问共享资源情况下为保证线程的安全,和对资源的原子性操作, 举个例子: i=i+1;其实是三部操作首先将i读取到线程的临时区存放,然后加一操作,最后将结果写回去.所谓锁机制就是保证一段程序在某段时间只有一个线程执行. 2. 可重入

ReentrantLock(重入锁)功能详解和应用演示

1. ReentrantLock简介 jdk中独占锁的实现除了使用关键字synchronized外,还可以使用ReentrantLock.虽然在性能上ReentrantLock和synchronized没有什么区别,但ReentrantLock相比synchronized而言功能更加丰富,使用起来更为灵活,也更适合复杂的并发场景. 2. ReentrantLock和synchronized的相同点 2.1 ReentrantLock是独占锁且可重入的 例子 public class Reentr

[图解Java]ReentrantLock重入锁

图解ReentrantLock 0. demo 我先给出一个demo, 这样大家就可以根据我给的这段代码, 边调试边看源码了. 还是那句话: 注意"My" , 我把ReentrantLock类 改名为了 "MyReentrantLock"类 , "Lock"类 改名为了"MyLock"类. 大家粘贴我的代码的时候, 把相应的"My"都去掉就好了, 否则会编译报错哦. import java.util.Sca

MyBatis简单源码分析1 - 环境搭建

本文以MyBatis独立使用的情形简单地分析MyBatis的源码,记录自己学习的过程 重要的Java代码如下: 主程序: 1 package com.suntao.learning.debug; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.util.List; 6 7 import org.apache.ibatis.io.Resources; 8 import org.apache.ib

SpringMvc流程分析,简单源码分析

SpringMvc的请求入口:web.xml中的DispatcherServlet <servlet> <servlet-name>springServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfi

死磕 java集合之ConcurrentHashMap源码分析(一)

开篇问题 (1)ConcurrentHashMap与HashMap的数据结构是否一样? (2)HashMap在多线程环境下何时会出现并发安全问题? (3)ConcurrentHashMap是怎么解决并发安全问题的? (4)ConcurrentHashMap使用了哪些锁? (5)ConcurrentHashMap的扩容是怎么进行的? (6)ConcurrentHashMap是否是强一致性的? (7)ConcurrentHashMap不能解决哪些问题? (8)ConcurrentHashMap中有哪