码迷,mamicode.com
首页 > 编程语言 > 详细

[图解Java]ReentrantLock重入锁

时间:2018-07-27 14:46:08      阅读:167      评论:0      收藏:0      [点我收藏+]

标签:out   stat   ant   clu   cep   ann   new   err   http   

图解ReentrantLock

0. demo

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

import java.util.Scanner;
import java.util.function.Supplier;

public class Main {
    static final Scanner scanner = new Scanner(System.in);
    static volatile String cmd = "";
    private static MyReentrantLock lock = new MyReentrantLock(true);

    public static void main(String[] args) {
        for (String name : new String[]{"1", "2", "3", "4", "5", "6"})
            new Thread(() -> func(() -> lock, name)).start();

        while (scanner.hasNext()) {
            cmd = scanner.nextLine();
        }
    }

    public static void func(Supplier<MyLock> myLockSupplier, String name) {
        blockUntilEquals(() -> cmd, "lock " + name);
        myLockSupplier.get().lock();
        System.out.println("获取了" + name + "号锁");
        blockUntilEquals(() -> cmd, "unlock " + name);
        myLockSupplier.get().unlock();
        System.out.println("释放了" + name + "号锁");
    }

    private static void blockUntilEquals(Supplier<String> cmdSupplier, final String expect) {
        while (!cmdSupplier.get().equals(expect))
            quietSleep(1000);
    }

    private static void quietSleep(int mills) {
        try {
            Thread.sleep(mills);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 使用例子在下面. 首先线程1申请了锁, 成功申请. 然后线程2申请了锁, 未申请到, 进入等待队列中. 线程3 和 线程4 也申请失败, 进入到等待队列中. 

随后释放了锁1, 然后锁2就获取到锁了. 然后释放了锁2, 锁3就获取到锁了...然后是锁4.  大概就是这个使用. 用我的这段代码配合着debug, 可以很清楚地调试出代码的执行流程.

技术分享图片

2. 开始图解ReentrantLock 

一个ReentrantLock()实例里只有一个sync成员变量.

技术分享图片

假设咱们创建了一个公平锁, 那么sync是FairSync类的实例.

sync实例里面有四个成员变量.

分别表示:

          1. state - 锁计数器

          2. exclusiveOwnerThread - 锁的持有线程

          3. head - `等待队列`的头结点.

          4. tail - 指向`等待队列`的最后一个元素

技术分享图片

现在锁是空闲状态.

当线程1申请了锁, 会把state置为1. 然后把锁的exclusiveOwnerThread指向自己(线程1). 这就算是持有锁了.其他线程无法再获取锁了.只能等线程1释放.

技术分享图片

 如果线程1在此对这个锁执行了lock()方法呢? 

那么就是锁的重入了, 也就是说这个线程再次进入(获取)了这个锁 会让state+1.

技术分享图片

 再重入呢?   那就再加1....

可以重入多少次呢?   可以重入, 直到整形int溢出为止...

技术分享图片

 接下来, 线程1还没释放锁呢, 线程2就想获取锁了. 那么会发生什么呢:

把线程2封装为一个Node类型的节点. 然后打算把这个Node放到`等待队列`中去.

这个时候`等待队列`才会被建立, 因为这个时候才需要`等待队列`, 这种叫懒初始化.

这个时候, `等待队列`的头结点产生了. 然后把`等待队列`的tail也指向head.

head或者tail 不为null, 表示`等待队列`被创立了.

head==tail 表示, `等待队列`为空, 里面没有`有效元素`.

技术分享图片

 `等待队列`有了. 线程2对应的Node也有了. 就差把这个Node插入到队尾了.

首先让tail指向线程2对应的Node.

然后分别维护两个Node的前驱和后继.(看下面紫色箭头)

技术分享图片

 已经将线程2对应的Node插入到`等待队列`的尾部了, 接下来让线程1对应的Node里的waitState等于-1

 技术分享图片

 之后线程2就可以安心的挂起了. 等线程1完全释放锁的时候,  就会唤醒线程2了.

为什么说是`完全释放`呢? 因为锁的的state现在等于3. 需要线程1 unlock()释放3次锁, 才算是完全释放.

 技术分享图片

 接下来, 线程1还没释放锁呢, (线程2也没轮到锁呢). 线程3就想获取锁了. 那么会发生什么呢:

首先会创建一个线程3对应的Node节点.

 技术分享图片

 

 然后让尾指针tail指向这个最新的Node.

然后维护前驱和后继(紫色箭头), 来维持双向链表.

技术分享图片

 接下来就会让新节点的前驱节点的waitStatus = -1.

-1表示, 有下一个节点等待被唤醒. 

技术分享图片

 然后线程3就可以安心的挂起了.

等线程2 抢到锁, 用完了释放后, 就会去唤醒线程3.

 技术分享图片

咱们让线程1 unlock() 一次.

state减1了.

此时, 锁并没有释放, 还是被线程1持有.

技术分享图片 

咱们再让线程1 unlock() 一次.

state减1了. 但仍然大于0.

此时, 锁并没有释放, 还是被线程1持有.

 技术分享图片

咱们再让线程1 unlock() 一次.

state减1了. 这回state等于0了. 表示完全释放了锁.

exclusiveOwnerThread也置为了null, 表示当前的锁不被任何线程持有.

技术分享图片 

准备唤醒下一个, 也就是`等待队列`的第一个元素(线程2)

 技术分享图片

线程2被唤醒

技术分享图片

然后锁的state被置为了1.

锁的exclusiveOwnerThread指向了线程2. 表示当前锁被线程2持有了.

技术分享图片

 既然线程1已经完全释放锁了. 那么就换线程2来当`等待队列`的头结点.

所以此时, 头结点的含义就是: 当前持有锁的线程对应的Node结点. 

 技术分享图片

 然后断开相应的前驱和后继, 让线程1对应的Node完全脱离`等待队列` .

 技术分享图片 

到此, 线程1释放后, 线程2 获取锁的步骤就都执行完了. 

接下来, 咱们让线程2释放锁.

state减1后等于0了.

于是锁就完全释放了. exclusiveOwnerThread就被置为null了.

技术分享图片

 然后是waitStatus被改回了0. 线程2对应的Node马上就要离开`等待队列`了

技术分享图片

 线程3被唤醒. 

技术分享图片

 让state=1, 并把锁的exclusiveOwnerThread指向自己. 表示线程3自己独占了这把锁.

 技术分享图片

 修改head指针, 并断开相应的前驱和后继链接, 让线程2对应的Node彻底离开`等待队列`

 技术分享图片

最后, 咱们让线程3释放锁.

state归零.

exclusiveOwnerThread清空.

锁空闲.

而head和tail仍然指向原先的Node. 以后等待队列的头结点就不需要重新初始化了.

技术分享图片

[图解Java]ReentrantLock重入锁

标签:out   stat   ant   clu   cep   ann   new   err   http   

原文地址:https://www.cnblogs.com/noKing/p/9376982.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!