在多线程场景中,如有些线程需要依赖另外线程的结果而继续执行,如多个线程处理请求,有的处理的快有点慢。快的需要等待慢的线程结果一起提交执行结果。都会涉及到线程间的通信,就是A线程告知B线程处理的结果是怎么样,B线程再执行对应逻辑。通信比较经典的就是采用等待通知模式,当然还有join,volilate等也可作为通信手段。本文重点讲下API层面的通信。
举个例子,三个线程T1,T2,MAIN的场景,T1因为条件不满足进入等待状态,等待其他线程的唤醒(可理解为孩子吃饭没辣条自己关上门在房间里等待辣条)。T2的工作就是唤醒T1线程,但是没满足T1的条件。所以T1还是会进入等待状态(可理解为爸爸为了骗孩子吃饭去敲门叫孩子,说有辣条了,但此时其实没辣条的,所以孩子出门看了一眼,又回去房间里等待了)。MAIN线程也是唤醒线程,并且还满足T1的条件,这时T1就会正常执行。(可理解为妈妈过了一段时间看不下去了,去买了辣条并叫孩子出来吃饭,孩子出来看到有辣条了就开心的吃起来饭)
(1)基于Object的wait-notify,此方法需要与syncronized代码块共用。因为底层是采用monitor对象的waitSet去实现。
@Slf4j
class WaitNotify {
private static boolean flag = false;
private static Object lock = new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized (lock) {
while (!flag) {
log.info("没辣条不想吃饭,进房间等待");
try {
lock.wait(); //释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("哈哈!有辣条吃饭真香");
}
},"孩子").start();
new Thread(()->{
synchronized (lock) {
log.info("爸爸给你买辣条了,出来吃饭");
lock.notify();
}
},"爸爸").start();
SleepUtils.sleep(2);
synchronized (lock) {
log.info("妈妈给你买辣条了,出来吃饭");
flag = true;
lock.notify();
}
}
}
(2)基于ReentrantLock的await,single。语法比较繁琐,需要先获取到lock,然后要建立一个休息室供线程休息,类似与monitor中的waitSet。最后需要unlock释放锁。
@Slf4j
class AwaitSingle{
private static boolean flag = false;
private static ReentrantLock lock = new ReentrantLock();
static Condition condition = lock.newCondition(); //休息室
public static void main(String[] args) {
new Thread(()-> {
lock.lock();
try {
while (!flag) {
log.info("没辣条不想吃饭,进房间等待");
try {
condition.await();//进入休息室等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("哈哈!有辣条吃饭真香");
} finally {
lock.unlock();
}
},"孩子").start();
new Thread(()->{
lock.lock();
try{
log.info("爸爸给你买辣条了,出来吃饭");
condition.signal(); //唤醒
} finally {
lock.unlock();
}
},"爸爸").start();
SleepUtils.sleep(2);
lock.lock();
try {
log.info("妈妈给你买辣条了,出来吃饭");
flag = true;
condition.signal();
}finally {
lock.unlock();
}
}
}
(3)基于LockSupport的park,unpark方法。此方法要注意的是如先调用unpark,则第一次park时不会被阻塞。第二次才会。但并不会unpark调了多次就可以使park失效多次,调用多次unpark也只会失效一次park。
@Slf4j
class Park{
private static boolean flag = false;
public static void main(String[] args) {
Thread t1 = new Thread(()-> {
while (!flag) {
log.info("没辣条不想吃饭,进房间等待");
LockSupport.park();
}
log.info("哈哈!有辣条吃饭真香");
},"孩子");
t1.start();
new Thread(()->{
log.info("爸爸给你买辣条了,出来吃饭");
LockSupport.unpark(t1);
},"爸爸").start();
SleepUtils.sleep(2);
log.info("妈妈给你买辣条了,出来吃饭");
flag = true;
LockSupport.unpark(t1);
}
}