CountdownLatch 主要作用在于协同多个线程之间的运行;常常使用在A线程启动后需要做一些初始化操作,然后会启动B线程。
请注意,B线程一定在A线程之后,但是当你之间new Thread().start() 时你并不能确保A一定先执行了,所以需要用到CountdownLatch来协同。
回到我们的课程代码:
先看这个部分,这个部分先不看传入的CountdownLatch;就单纯看当前方法中创建的CountdownLatch;它完成了一个事情,那就是启动Listener线程后当前线程等待,一直等待到Listener线程真正运行起来进入到run方法中。
然后我们再看传入进来的receiveLatch,这个实例会传递给Listener线程内部:
它的作用也很简单,就是在当前监听器收到了一个回送广播时,进行一次衰减,以便被外部等待者感知到,进入后续步骤。
那么我们回到外层的位置:
这个地方也就能够看懂了;首先我们创建一个receiveLatch的拦截实例,然后将实例传递给监听器线程,当线程收到一份广播回送数据时则进行一次衰减,以便解除当前的阻塞:
receiveLatch.await(timeout, TimeUnit.MILLISECONDS);
但是请注意,在这里我们不是直接无限死等待,而是有时间的等待,这意味着在等待时间内,没有进行衰减操作,那么我也不会继续阻塞了,我会自己触发超时往后走。而衰减操作就是有数据到达时进行,简单来说就是等待时间内没有数据到达我就不等了。若在等待时间内(10秒)第3秒就收到了回送,那么我立刻就往后走了,无需等待了。
然后回到你的疑问,如果不用CountdownLatch会怎样;假如我们全部移除掉;然后来说说一些异常情况:
1. Listener调用start后当前主线程就直接往后走了,开始去发送信息了;此时可能网络回来数据了,但是Listener还没真正运行起来也就没有进入run方法中,所以可能会丢失掉当前的回送数据;所以我们需要使用第一个startDownLatch用以确保这样的情况不会发生。
2. 假如没有receiveLatch,那么在启动Listener线程后,我们就去发送广播,然后我们就去拿数据,就返回了;此时我们的服务器极大可能并未返回数据,所以也会丢失服务器返回的数据。所以我们需要借助receiveLatch进行等待,当然不是死等待,因为有可能没有服务器给我们返回数据,所以也会有永远等不到的情况。