Object 的 wait / notify / notifyAll 几个方法可以实现对线程的挂起和唤醒。ReentrantLock 也可以,不过要借助我们的 Condition 条件监视器,而且 Object 唤醒部分县城是随机唤醒,Condition 使得线程唤醒是可控的,可以指定唤醒部分线程。
首先我们用 ReentrantLock 和 Condition 实现 Object 中对应的方法 |
public class Myservice {
private Lock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void await(){
try{
lock.lock();
System.out.println(Thread.currentThread().getName()+"await begin");
condition.await();
System.out.println(Thread.currentThread().getName()+"await end");
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signal(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+" signal begin");
condition.signal();
System.out.println(Thread.currentThread().getName()+" signal end");
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signalAll(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"signalAll begin");
condition.signalAll();
System.out.println(Thread.currentThread().getName()+"signalAll end");
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
在来一个测试线程类:
public class TestConditionThread extends Thread{
private Myservice service ;
public TestConditionThread(Myservice service){
this.service = service;
}
@Override
public void run() {
service.await();
}
}
在启动线程并且测试:
public class CliTest {
public static void main(String[] args) throws InterruptedException {
Myservice myservice = new Myservice();
TestConditionThread t = new TestConditionThread(myservice);
t.setName("A:");
t.start();
TestConditionThread t1 = new TestConditionThread(myservice);
t1.setName("B:");
t1.start();
TestConditionThread t2 = new TestConditionThread(myservice);
t2.setName("C:");
t2.start();
Thread.sleep(1000);
myservice.signal();
myservice.signal();
myservice.signal();
}
}
我们可以发现,被唤醒的线程是有顺序的,并不是随机唤醒的。这是因为这个阻塞队列在AQS实现的是一个FIFO队列。先被阻塞的,先被唤醒。
signalAll 就是顺序的把队列中的线程全部唤醒,自己可以试下。
唤醒部分线程实现 |
如果想要单独唤醒部分线程,这时候就有必要使用多个Condition对象了,也就是Condition对象可以唤醒部分指定线程,有助于提升程序效率。可以先对线程分组,然后再唤醒组中的指定线程。
Demo |
public class NotifyOtherThreadService {
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
public void awaitA(){
try{
lock.lock();
System.out.println(Thread.currentThread().getName()+": await A begin");
conditionA.await();
System.out.println(Thread.currentThread().getName()+": await A end");
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void awaitB(){
try{
lock.lock();
System.out.println(Thread.currentThread().getName()+": await B begin");
conditionB.await();
System.out.println(Thread.currentThread().getName()+": await B end");
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
/**
* 唤醒A组中的所有线程
*/
public void signalAll_A(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+": signalAll A begin");
conditionA.signalAll();
System.out.println(Thread.currentThread().getName()+": signalAll A end");
}finally {
lock.unlock();
}
}
/**
* 唤醒B组中的所有线程
*/
public void signalAll_B(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+": signalAll B begin");
conditionB.signalAll();
System.out.println(Thread.currentThread().getName()+": signalAll B end");
}finally {
lock.unlock();
}
}
}
测试类:
public class NotiFyOtherThreadTest {
public static void main(String[] args) throws InterruptedException {
NotifyOtherThreadService service = new NotifyOtherThreadService();
new Thread(()->{
service.awaitA();
},"A").start();
new Thread(()->{
service.awaitB();
},"B").start();
Thread.sleep(1000);
service.signalAll_B();
}
}
结果
根据结果可以知道,B组线程被唤醒,而A组线程还在阻塞之中。
详细源码分析需要明天的Condition与AQS了,不过笔者已经分析过一波ReentrantLock的源码,有兴趣可以戳https://blog.csdn.net/WeiJiFeng_/article/details/81390935