天天看点

java 线程等待队列_Java 并发编程之同步队列与等待队列

Java 并发编程之同步队列与等待队列

在上一篇博客中,我简单的介绍了对 Condition 和 ReentrantLock 的使用,但是想要更好的掌握多线程编程,单单会用是不够的。这篇我会针对 Condition 方法中的 await 和 signal 的实现原理来梳理一下我的理解。

首先我们需要了解同步队列和等待队列的概念。简单的理解是同步队列存放着竞争同步资源的线程的引用(不是存放线程),而等待队列存放着待唤醒的线程的引用。

同步队列中存放着一个个节点,当线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成为一个节点并将其加入同步队列,首节点表示的获取同步状态成功的线程节点。

java 线程等待队列_Java 并发编程之同步队列与等待队列

Condition 维护着一个等待队列与同步队列相似。主要针对 await 和 signal 的操作。

java 线程等待队列_Java 并发编程之同步队列与等待队列

现在不理解没关系,下面用实例来认识一下它们在多线程中是如何使用的。这里实现了三个多线程的 run 方法。A 线程输出 A 然后通知 B, 然后 B 通知 C。

public static class ThreadA extends Thread{

@Override

public void run(){

try{

lock.lock();

System.out.println("A进程输出" + " : " + ++index);

conditionB.signal();

conditionA.await();

}catch (Exception e){

e.printStackTrace();

}finally {

lock.unlock();

}

}

}

public static class ThreadB extends Thread{

@Override

public void run(){

try{

lock.lock();

System.out.println("B进程输出" + " : " + ++index);

conditionC.signal();

conditionB.await();

}catch (Exception e){

e.printStackTrace();

}finally {

lock.unlock();

}

}

}

public static class ThreadC extends Thread{

@Override

public void run(){

try{

lock.lock();

System.out.println("C进程输出" + " : " + ++index);

conditionA.signal();

conditionC.await();

}catch (Exception e){

e.printStackTrace();

}finally {

lock.unlock();

}

}

}

public class CondtionTest {

public static ReentrantLock lock = new ReentrantLock();

public static Condition conditionA = lock.newCondition();

public static Condition conditionB = lock.newCondition();

public static Condition conditionC = lock.newCondition();

public static int index = 0;

public static void main(String[] args){

ThreadA threadA = new ThreadA();

ThreadB threadB = new ThreadB();

ThreadC threadC = new ThreadC();

threadA.start();//(1)

threadB.start();//(2)

threadC.start();//(3)

}

}

当 (1)(2)(3) 三个线程被调用时,因为三个线程同时竞争 lock,这里假设线程 A 拿到了 lock(线程 A 虽然是看起来是先 start(),但是正在的调用还是看调度程序的,所以这里只能假设是 A 线程拿到同步资源)。首节点表示的是正在操作同步资源的线程。所以现在的同步队列是:

java 线程等待队列_Java 并发编程之同步队列与等待队列

接着线程 A 输出了:“A 进程输出 : 1”。然后调用 conditionB.signal(),其实这一步的 signal 是没什么意义的,因为 conditionB 现在没有线程是可以被唤醒的。

当 conditionA.await() 被执行到的时候,线程 A 同步队列中被移除,对应操作是锁的释放; 线程 A(节点 A) 接着被加入到 ConditionA 等待队列,因为线程需要 singal 信号。

同步队列:

java 线程等待队列_Java 并发编程之同步队列与等待队列

A 等待队列:

java 线程等待队列_Java 并发编程之同步队列与等待队列

现在在同步队列中的首节点是 B 节点,那么 B 线程占用了同步资源就可以开始运行了。先是输出 “B 进程输出 : 2”,同样的 signal 操作也是没有意义的,因为 conditionC 是没有可以被唤醒的线程。当 conditionB.await() 被执行到的时候,线程 B 同步队列中被移除,线程 B(节点 B)接着被加入到 ConditionB 等待队列

同步队列:

java 线程等待队列_Java 并发编程之同步队列与等待队列

B 等待队列:

java 线程等待队列_Java 并发编程之同步队列与等待队列

终于轮到了 C 线程占用同步资源了,再输出 “C 进程输出:3” 之后,调用 conditionA.signal(),注意这个 signal 是有用的

因为在 conditionA 的等待队列中 A 线程是在等待的,把它取出来加入到同步队列中去竞争,但是这个时候线程 A 还没唤醒。首节点还是 C。

同步队列:

java 线程等待队列_Java 并发编程之同步队列与等待队列

接着 conditionC.await() 被执行。线程 C 同步队列中被移除,线程 C(节点 C) 接着被加入到 ConditionC 等待队列

同步队列:

java 线程等待队列_Java 并发编程之同步队列与等待队列

C 等待队列:

java 线程等待队列_Java 并发编程之同步队列与等待队列

注意到同步队列中的首节点已经变回了节点 A 了。所以线程 A 在刚刚等待的地方继续执行,最后释放了 lock。但是线程 B 和线程 C 最后也没有其他线程去唤醒,状态一直为 WAITING,而线程 A 的状态为 TERMINATED