介绍
一种常见的场景,当一个用户请求服务器,服务器为了响应,要进行一系列的操作,有可能需要去调用多个接口,等待各个接口返回结果,而各个接口之间相互独立,为了提高效率,一般会使用异步的方式。当所有异步线程都执行完毕后,通知主线程,主线程进一步执行下一步的逻辑,最终响应。那么主线程如何知道所有线程都完成了呢?本文介绍一种闭锁,让多个线程执行完后通知主线程,若未执行完,主线程阻塞,等待其执行完毕后再进行下一步操作。
原理
本文将例举一个常见的场景,来说明闭锁的原理。
例如:
BOSS下有Worker1,Worker2,Worker3,各个Worker都做着不同的事情,Boss准备当3个Worker都完成后才发工资。三个Worker同时开工。当一个Worker完成后,通知Boss,Boss知道后,就知道了还有2个Worker没有工作完,当再收到一个Worker的通知后,就知道还有1个Worker没工作,直到最后一个完成工作的Worker通知Boss,Boss才发工资。
代码实现
import java.util.concurrent.atomic.AtomicInteger;
public class Boss {
public static void main(String[] args) {
AtomicInteger latch = new AtomicInteger(3);
new Thread(new Worker(latch, 1000)).start();
new Thread(new Worker(latch, 2000)).start();
new Thread(new Worker(latch, 3000)).start();
while(latch.intValue() != 0){
synchronized (latch) {
try {
latch.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("发放工资");
}
private static class Worker implements Runnable{
private AtomicInteger latch;
private long sleepTime;
public Worker(AtomicInteger latch,long sleepTime){
this.latch = latch;
this.sleepTime = sleepTime;
}
@Override
public void run() {
try {
Thread.sleep(sleepTime); //模拟Worker的工作花掉的时间
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
latch.decrementAndGet();
synchronized (latch) {
latch.notifyAll();
}
}
}
}
}
输出结果:发放工资
以上为笔者自己模拟实现的闭锁,事实上,在JUC中,提供了CountDownLatch的一个类,用来简化闭锁的操作。
使用CountDownLatch实现闭锁如下:
import java.util.concurrent.CountDownLatch;
public class Boss {
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(3);
new Thread(new Worker(latch, 1000)).start();
new Thread(new Worker(latch, 2000)).start();
new Thread(new Worker(latch, 3000)).start();
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发放工资");
}
private static class Worker implements Runnable{
private CountDownLatch latch;
private long sleepTime;
public Worker(CountDownLatch latch,long sleepTime){
this.latch = latch;
this.sleepTime = sleepTime;
}
@Override
public void run() {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
latch.countDown();
}
}
}
}
输出结果:发放工资。
总结
本文简要介绍了闭锁的原理,并对其进行了代码实现。在介绍中,提出了接口的异步调用和闭锁的解决方法,但在实际场景中,往往需要判断其异步调用是否成功,此文中的两种方式都无法实现该需求,因此,接下来将提出另一种线程的构造方法,通过实现Callable接口构造线程,详情请参考笔者关于Callable的文章。