[图解Java]Condition

图解Condition

0. demo

我先给出一个demo, 这样你们就能够根据我给的这段代码, 边调试边看源码了. 仍是那句话: 注意"My" , 我把ReentrantLock类 更名为了 "MyReentrantLock"类 , "Lock"类 更名为了"MyLock"类. 你们粘贴个人代码的时候, 把相应的"My"都去掉就行了, 不然会编译报错哦.java

import java.util.Scanner;
import java.util.concurrent.locks.Condition;
import java.util.function.Supplier;

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

    public static void main(String[] args) {
        for (String name : new String[]{"w1", "w2", "w3", "w4", "w5", "w6"})
            new Thread(() -> func(() -> lock, name)).start();
        new Thread(() -> signalOne(() -> lock, "s")).start();

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

    public static void func(Supplier<MyLock> myLockSupplier, String name) {
        blockUntilEquals(() -> cmd, name);
        myLockSupplier.get().lock();

        System.out.println(name + "阻塞等待...");
        try {
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("释放了" + name);

        myLockSupplier.get().unlock();
    }

    public static void signalOne(Supplier<MyLock> myLockSupplier, String name) {
        while (true) {
            blockUntilEquals(() -> cmd, name);
            myLockSupplier.get().lock();
            condition.signal();
            System.out.println("通知唤醒了一个等待...");
            myLockSupplier.get().unlock();
        }
    }

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

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

    private static void clearCmd() {
        cmd = "";
    }
}

使用例子在下面. ui

首先输入w1, 让线程1执行await() . 而后输入w2, 让线程2执行await(). 而后输入w3, 让线程3执行await().线程

接下来输入3次 s, 没输入一次s, 并按下回车, 就会signal通知一个await等待.3d

1. 开始图解Condition

想用ReentrantLock的Condition, 那么就首先要有个ReentrantLock锁.调试

实例化一个锁, ReentrantLock里只有一个成员变量sync.blog

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

分别表示:get

          1. state - 锁计数器cmd

          2. exclusiveOwnerThread - 锁的持有线程源码

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

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

 而后我们实例化了一个Condition. 

 当我们输入w1后, 第一个线程就申请了锁, 而且申请成功.

 

 而后就执行到了await()方法.

将线程1封装为Node节点, 而后waitState置为-2.  -2的含义是Condition.

 await()方法内部的第一个步骤就是把当前线程(线程1)插入到了`条件队列`中.

 

而后就开始释放当前线程(线程1)的锁了, 并且是彻底释放, 一次就释放掉所有重入次数哦, 也就是直接让state等于0. 

释放完锁了, 而后挂起线程1.

 

而后让线程2进行await.( 也就是前面的demo程序中在控制台输入了w2.)

线程2执行await()以前固然是先获取锁了.

因为此时, 锁是空闲的. 因此线程2成功获取到了锁. 淡橙色的阴影部分为变化的内容:

  

获取锁以后, 线程2就该执行await()了:

 

 如上图, 将线程封装为Node, 而后尾插到`条件队里`中, 只是await() 方法的第一步.

而后的操做, 就是彻底释放线程2的锁, 而后挂起线程.

 

 

若是这个时候我们在上面demo程序的控制台输入"s", 那么就会让线程s 申请锁, 申请成功后, 就会执行signal.

首先是线程s申请锁成功:

 线程s成功获取了锁之后, 就是该执行signal()了.

 首先将`条件队列`里的第一个节点脱离出来:

 而后把waitState从-2改成0 :

随后要作的就是把从`条件队列`中脱离出来的Node(就是线程1对应的Node节点), 尾插到`等待队列`中.

可是`等待队列`此时还未被初始化, 因此插入到`等待队列`以前, 要把`等待队列`初始化了. 见下图:

 `等待队列`初始化完了. 接下来就是把线程1对应的Node, 尾插到`等待队列`中了:

 

而后将当前尾插的那个节点的前驱的waitState置为-1.  -1表示下一个节点等待着被唤醒. 

 

接下来就是线程s会执行到unlock(). 而后就会释放锁, 以后就是唤醒`等待队列`中的第一个线程.

如此就介绍完了signal()

 

一个signal命令, 就把一个await的线程从`条件队列中`移到了`等待队列`中. 到了等待队列中以后, 剩下的就是跟"锁解锁后, 唤醒下一个执行"这样的步骤同样了.

我以为await方法就是将线程尾插到`条件队列`中. signal()方法就是把条件队列中的第一个元素, 尾插入到`等待队列`中.

因此我以为没必要往下分析了.

也多是我理所固然了, 有问题的话以后再补充.

相关文章
相关标签/搜索