[图解Java]Condition

图解Condition

0. demo

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

1import java.util.Scanner; 2import java.util.concurrent.locks.Condition; 3import java.util.function.Supplier; 4 5public class ConditionTest { 6 static final Scanner scanner = new Scanner(System.in); 7 static volatile String cmd = ""; 8 private static MyReentrantLock lock = new MyReentrantLock(true); 9 private static Condition condition = lock.newCondition(); 10 11 public static void main(String[] args) { 12 for (String name : new String[]{"w1", "w2", "w3", "w4", "w5", "w6"}) 13 new Thread(() -> func(() -> lock, name)).start(); 14 new Thread(() -> signalOne(() -> lock, "s")).start(); 15 16 while (scanner.hasNext()) { 17 cmd = scanner.nextLine(); 18 } 19 } 20 21 public static void func(Supplier<MyLock> myLockSupplier, String name) { 22 blockUntilEquals(() -> cmd, name); 23 myLockSupplier.get().lock(); 24 25 System.out.println(name + "阻塞等待..."); 26 try { 27 condition.await(); 28 } catch (InterruptedException e) { 29 e.printStackTrace(); 30 } 31 System.out.println("释放了" + name); 32 33 myLockSupplier.get().unlock(); 34 } 35 36 public static void signalOne(Supplier<MyLock> myLockSupplier, String name) { 37 while (true) { 38 blockUntilEquals(() -> cmd, name); 39 myLockSupplier.get().lock(); 40 condition.signal(); 41 System.out.println("通知唤醒了一个等待..."); 42 myLockSupplier.get().unlock(); 43 } 44 } 45 46 private static void blockUntilEquals(Supplier<String> cmdSupplier, final String expect) { 47 while (!cmdSupplier.get().equals(expect)) 48 quietSleep(1000); 49 clearCmd(); 50 } 51 52 private static void quietSleep(int mills) { 53 try { 54 Thread.sleep(mills); 55 } catch (InterruptedException e) { 56 e.printStackTrace(); 57 } 58 } 59 60 private static void clearCmd() { 61 cmd = ""; 62 } 63} 64

使用例子在下面. 

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

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

1. 开始图解Condition

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

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

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

分别表示:

          1. state - 锁计数器

          2. exclusiveOwnerThread - 锁的持有线程

          3. head -

1`
等待队列
1`
的头结点.

          4. tail - 指向

1`
等待队列
1`
的最后一个元素

 然后咱们实例化了一个Condition. 

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

 

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

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

 await()方法内部的第一个步骤就是把当前线程(线程1)插入到了

1`
条件队列
1`
中.

 

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

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

 

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

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

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

  

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

 

 如上图, 将线程封装为Node, 然后尾插到

1`
条件队里
1`
中, 只是await() 方法的第一步.

然后的操作, 就是完全释放线程2的锁, 然后挂起线程.

 

 

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

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

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

 首先将

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

 然后把waitState从-2改为0 :

随后要做的就是把从

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

但是

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

 

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

 

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

 

接下来就是线程s会执行到unlock(). 然后就会释放锁, 之后就是唤醒

1`
等待队列
1`
中的第一个线程.

如此就介绍完了signal()

 

一个signal命令, 就把一个await的线程从

1`
条件队列中
1`
移到了
1`
等待队列
1`
中. 到了等待队列中之后, 剩下的就是跟"锁解锁后, 唤醒下一个执行"这样的步骤一样了.

我觉得await方法就是将线程尾插到

1`
条件队列
1`
中. signal()方法就是把条件队列中的第一个元素, 尾插入到
1`
等待队列
1`
中.

所以我觉得不必往下分析了.

也可能是我理所当然了, 有问题的话之后再补充.

转载于:https://www.cnblogs.com/noKing/p/9380091.html

代码交流 2021