• ReentrantLock的功能详解与理解


    AQS

    全称是AbstractQueuedSynchronizer,是阻塞锁和相关的同步器工具的框架

    特点
    1、用state属性来表示资源的状态(分为独享状态与共享模式)
    getState - 获取state状态
    setState - 设置state状态
    compareAndSetState - 乐观锁机制设置state状态
    独占模式是只有一个线程能够访问资源,而共享模式允许多个线程访问资源
    2、提供了FIFO的等待队列,类似于Monitor的EntryList
    3、条件变量来实现等待、唤醒机制,支持多个条件变量,类似Monitor的WaitSet

    子类主要实现这些方法

    tryAcquire 获取锁
    tryRelease 释放锁
    tryAcquireShared
    tryReleaseShared
    isHeldExclusively

    ReentrantLockd概述

    相比synchronized具备如下特点
    1、可中断
    2、可以设置超时时间
    3、可以设置为公平锁
    4、支持多个条件变量
    与synchronized一样都支持可重入

    可重入

    可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁。
    如果是不可重入锁,那么第二次获取锁的时候,自己也会被锁挡住

    public class Test1 {
        public static void main(String[] args) {
            ReentrantLock lock = new ReentrantLock();
            lock.lock();
            try {
                System.out.println("lock1~");
                lock.lock();
                try {
                    System.out.println("lock2~");
                }finally {
                    lock.unlock();
                }
            }finally {
                lock.unlock();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    可打断

    在获取锁的时候可被打断避免一直阻塞等待获取锁

    public class Test1 {
        public static void main(String[] args) {
            ReentrantLock lock = new ReentrantLock();
            Thread t1 = new Thread(() -> {
                try {
                    lock.lockInterruptibly();
                    System.out.println("lock1获取到了锁");
                } catch (InterruptedException e) {
                    System.out.println("lock1中被打断了");
                }
            });
            lock.lock(); // 主线程获取到锁
            t1.start(); // t1线程启动
            //打断
            t1.interrupt();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    可超时

    与可打断类似,只是打断是被动,超时是主动,超时了就自动不获取锁了

    public class Test1 {
        public static void main(String[] args) {
            ReentrantLock lock = new ReentrantLock();
            Thread t1 = new Thread(() -> {
                try {
                    boolean flag = lock.tryLock(5, TimeUnit.SECONDS);
                    if (flag){
                        System.out.println("lock1获取到了锁");
                    }else {
                        System.out.println("lock1没有获取到锁");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            lock.lock(); // 主线程获取到锁
            t1.start(); // t1线程启动
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    可设置公平锁

    不容易代码实现
    使用公平锁是为了解决饥饿但是会降低并发度

    可设置多个条件变量

    ReentrantLockd相比synchronized,ReentrantLockd可支持多个条件变量,这好比synchronized是那些不满足条件的线程都在一间休息室里面等,而ReentrantLockd支持多间休息室

    await前需要获得锁
    await执行后,会释放锁,进入condition等待
    await的线程被唤醒去重新竞争锁
    竞争锁成功后执行await后的

    在这里插入图片描述

    state 就是加锁状态
    head 就是等待队列的头
    tail 就是等待队列的尾
    owner 就是当前是那个线程,全名为ExclusiveOwnerThread我这里简写的

    加锁流程

    最开始没有线程竞争,加锁成功

    final void lock() {
         if (compareAndSetState(0, 1))
         // 加锁成功
               setExclusiveOwnerThread(Thread.currentThread());
         else
         // 加锁失败
              acquire(1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    第二个线程来竞争 CAS失败,进入acquire(1)

        public final void acquire(int arg) {
        	// 尝试获取锁失败,所以!tryAcquire(arg)为true
        	//acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 创建一个节点对象然后放入等待队列中
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    进入acquireQueued方法

        final boolean acquireQueued(final Node node, int arg) {
            boolean failed = true;
            try {
                boolean interrupted = false;
                for (;;) {
                    final Node p = node.predecessor();
                    // 如果是第二个节点再尝试一次
                    if (p == head && tryAcquire(arg)) {
                        setHead(node);
                        p.next = null; // help GC
                        failed = false;
                        return interrupted;
                    }
                    //前驱节点waitStatus改为-1代表有责任唤醒下一个节点
                    if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                        interrupted = true;
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    蓝色三角形为Node是waitStatus状态,其中0为默认状态
    Node的创建是懒惰的
    其中第一个Node为哨兵或者哑元用来占位并不关联线程

    在这里插入图片描述
    在这里插入图片描述
    然后当前线程Thread-1 阻塞住

    在这里插入图片描述
    假如有多个线程经历上述过程

    在这里插入图片描述
    Thread-0 释放锁

            protected final boolean tryRelease(int releases) {
                int c = getState() - releases; // 这就是可重入的原因,当前线程如果是获取锁的线程state++
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                boolean free = false;
                if (c == 0) {
                    free = true;
                    setExclusiveOwnerThread(null);
                }
                setState(c);
                return free;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述
    当前队列不为空唤醒下一个

        public final boolean release(int arg) {
            if (tryRelease(arg)) {
                Node h = head;
                if (h != null && h.waitStatus != 0)
                	// 唤醒下一个节点
                    unparkSuccessor(h);
                return true;
            }
            return false;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    唤醒Thread-1

    在这里插入图片描述
    因为我们这是非公平锁,如果Thread-1在获取的时候Thread-4来获取,Thread-4抢夺成功

    在这里插入图片描述

    可重入原理

    加锁

            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()) { // 判断是否当前是当前线程是否是owner线程
                    int nextc = c + acquires;
                    if (nextc < 0) // overflow
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                return false;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    释放锁

            protected final boolean tryRelease(int releases) {
                int c = getState() - releases; // 释放锁state减少
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                boolean free = false;
                if (c == 0) {
                    free = true;
                    setExclusiveOwnerThread(null);
                }
                setState(c);
                return free;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    可打断原理

    抛出异常

        public final void acquireInterruptibly(int arg)
                throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            if (!tryAcquire(arg))
                doAcquireInterruptibly(arg);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
      private void doAcquireInterruptibly(int arg)
            throws InterruptedException {
            final Node node = addWaiter(Node.EXCLUSIVE);
            boolean failed = true;
            try {
                for (;;) {
                    final Node p = node.predecessor();
                    if (p == head && tryAcquire(arg)) {
                        setHead(node);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                    if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                        // 抛出异常
                        throw new InterruptedException();
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    公平锁原理

            protected final boolean tryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                	// hasQueuedPredecessors() 判断队列中是否有其它线程等待
                    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");
                    setState(nextc);
                    return true;
                }
                return false;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    条件变量实现原理

    每个条件变量都对应着一个等待队列,其实现类就是ConditionObject

    waitState标志位设置为-2
    并且调用fullRelease方法释放线程上的锁,避免重入时候多把锁没释放完

    在这里插入图片描述

  • 相关阅读:
    vue基础知识和原理(一)
    基于FPGA的移相波束形成verilog实现
    redis 不同部署方式性能测试
    菜单栏-JS防抖
    Http客户端OkHttp的基本使用
    如何升级到 Docker Compose v2
    Ubuntu搭建FTP服务
    深入理解synchronized关键字
    系统性学习vue-vue3
    特殊类型动归--区间动归与环形动归
  • 原文地址:https://blog.csdn.net/m0_74787523/article/details/127736985