ReentrantLock原理详解

ReentrantLock原理:

ReentrantLock简介:

ReentrantLock是可重入的独占锁,同时只能有一个线程可以获取该锁,其他获取该锁的线程会被阻塞而被放入该锁的AQS阻塞队列里面

AQS简介

AbstractQueuedSynchronizer简称AQS

AQS是一个用于构建锁和同步容器的框架

事实上concurrent包内许多类都是基于AQS构建,例如ReentrantLock,Semaphore,CountDownLatch,ReentrantReadWriteLock,FutureTask等。

AQS解决了在实现同步容器时设计的大量细节问题。

ReentrantLock原理

ReentrantLock 是基于 AQS 实现的,AQS 即 AbstractQueuedSynchronizer 的缩写,这个是个内部实现了两个队列的抽象类,分别是同步队列条件队列

  • 同步队列是一个双向链表,里面储存的是处于等待状态的线程,正在排队等待唤醒去获取锁
  • 条件队列是一个单向链表,里面储存的也是处于等待状态的线程,只不过这些线程唤醒的结果是加入到了同步队列的队尾

AQS 所做的就是管理这两个队列里面线程之间的等待状态——唤醒的工作。

在同步队列中,还存在 2 中模式,分别是独占模式和共享模式,这两种模式的区别就在于 AQS 在唤醒线程节点的时候是不是传递唤醒,这两种模式也对应分别独占锁和共享锁。

ReentrantLock实现的就是AQS 的独占模式,并且这种独占模式支持公平锁和非公平锁,两者的实现类似。

AQS 是一个抽象类,所以不能直接实例化,当我们需要实现一个自定义锁的时候可以去继承 AQS 然后重写获取锁的方式和释放锁的方式还有管理state,而 ReentrantLock 就是通过重写了 AQS 的 tryAcquire 和 tryRelease 方法实现的 lock 和 unlock

ReentrantLock 结构如下图所示:
在这里插入图片描述

首先 ReentrantLock 实现了 Lock 接口,然后有 3 个内部类,其中 Sync 内部类继承自 AQS ,另外的两个内部类继承自 Sync ,这两个类分别是用来公平锁和非公平锁的。通过 Sync 重写的方法tryAcquire 、 tryRelease 可以知道, ReentrantLock 实现的是 AQS 的独占模式,也就是独占锁,这个锁是悲观锁。

ReentrantLock是可重入的独占锁,同时只能有一个线程可以获取该锁,其他获取该锁的线程会被阻塞而被放入该锁的AQS阻塞队列里面

ReentrantLock的类图如下,从类图可以看到,ReentrantLock最终还是使用AQS来实现的,并且根据参数来决定其内部是一个公平还是非公平锁,默认是非公平锁
在这里插入图片描述

ReentrantLock实现方法探究

获取锁:

void lock()方法

当一个线程调用该方法时,说明该线程希望获取该锁。

如果锁当前没有被其他线程占用并且当前线程之前没有获取该锁,则当前线程会获取到该锁,然后设置当前锁的拥有者为当前线程,并设置AQS的状态值为1,然后直接返回。

如果当前线程之前已经获取过该锁,则这次只是简单地把AQS的状态值加1后返回。如果该锁已经被其他线程持有,则调用该方法的线程会被放入AQS队列后阻塞挂起。

void lockInterruptibly()方法

该方法与lock()方法类似,它的不同在于,它对中断进行响应,就是当前线程在调用该方法时,如果其他线程调用了当前线程的interrupt()方法,则当前线程会抛InterruptedException异常,然后返回。

boolean tryLock()方法

尝试获取锁,如果当前该锁没有被其他线程持有,则当前线程获取该锁并返回true,否则返回false

值得注意的是,该方法不会引起当前线程阻塞

boolean tryLock(long timeout, TimeUnit unit)方法

尝试获取锁,与tryLock()的不同之处在于,它设置了超时时间,如果超时时间到,没有获取到该锁则返回false。

释放锁:

void unlock()方法

尝试释放锁,如果当前线程持有该锁,则调用该方法会让线程对该线程持有的AQS状态值减1,如果减去1后当前状态值为0,则当前线程会释放该锁,否则仅仅减1而已。

如果当前线程没有持有该锁而调用了该方法则会抛出IllegalMonitorStateException异常。

综合案例

代码如下:

package com.yyl.locktest;

import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockList {
    
    
    // 线程不安全的list
    private ArrayList<String> arrayList = new ArrayList<String>();
    // 独占锁
    private volatile ReentrantLock lock = new ReentrantLock();

    // 添加元素
    public void add(String e) {
    
    
        lock.lock();
        try {
    
    
            arrayList.add(e);
        }finally {
    
    
            lock.unlock();
        }
    }

    // 删除元素
    public void remove(String e) {
    
    
        lock.lock();
        try {
    
    
            arrayList.remove(e);
        }finally {
    
    
            lock.unlock();
        }
    }

    // 获取数据
    public String get(int index) {
    
    
        lock.lock();
        try {
    
    
            return arrayList.get(index);
        }finally {
    
    
            lock.unlock();
        }
    }

    public static void main(String[] args) {
    
    
        ReentrantLockList rt = new ReentrantLockList();
        for (int i=0;i<10;i++){
    
    
            int num = i;
            System.out.println("获取锁:"+rt.lock.tryLock());
            new Thread(()->{
    
    
                rt.add("number:"+ num);
            }).run();
        }

        rt.arrayList.stream().forEach((item)->{
    
    
            System.out.println(item);
        });
        System.out.println(rt.get(4));
        System.out.println(rt.get(7));
    }
}

结果如下:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45525272/article/details/127302441