子线程常用来执行耗时任务,有时主线程需要子线程执行完毕后的结果再执行,现在来探讨实现该场景的几种实现方式
一、join()
说明:Thread中的join()方法就是同步,它使得线程之间由并行执行变为串行执行。
测试代码如下:
object ThreadTest {
@JvmStatic
fun main(args: Array<String>) {
val t = Thread(SubThread())
t.start()
mainWork()
t.join()
println("Now all thread done!")
}
// 子线程
class SubThread : Runnable {
override fun run() {
println("Sub thread is starting!")
try {
Thread.sleep(5000L)
} catch (e: InterruptedException) {
e.printStackTrace()
}
println("Sub thread is stopping!")
}
}
// 主线程
private fun mainWork() {
println("Main thread start work!")
//sleep
Thread.sleep(2000L)
println("Main Thread work done!")
}
}
二、CountDownLatch
说明:
CountDownLanch
是一个倒数计数器, 给一个初始值(>=0), 然后每一次调用countDown就会减1, 这很符合等待多个子线程结束的场景: 一个线程结束的时候, countDown一次, 直到所有的线程都countDown了 , 那么所有子线程就都结束了。
常用方法:
await: 会阻塞等待计数器减少到0位置. 带参数的await是多了等待时间.
countDown: 将当前的计数减1
getCount(): 返回当前的计数
显而易见, 我们只需要在子线程执行之前, 赋予初始化countDownLanch, 并赋予线程数量为初始值.
每个线程执行完毕的时候, 就countDown一下.主线程只需要调用await方法, 可以等待所有子线程执行结束。
示例:
object CountDownLatchTest {
@JvmStatic
fun main(args: Array<String>) {
//定义线程数
val subThreadNum = 5
//取得一个倒计时器,从5开始
val countDownLatch = CountDownLatch(subThreadNum)
//依次创建5个线程,并启动
for (i in 0 until subThreadNum) {
Thread(SubThread(2000L * (i + 1), countDownLatch)).start()
}
mainWork()
// 等待所有的子线程结束
countDownLatch.await()
println("Now all thread done!")
/**
* 运行结果
* Main thread start work!
Sub thread is starting!
Sub thread is starting!
Sub thread is starting!
Sub thread is starting!
Sub thread is starting!
Main Thread work done!
Now all thread done!
*/
}
// 子线程
class SubThread(var workTime:Long,var countDownLatch: CountDownLatch) : Runnable {
override fun run() {
println("Sub thread is starting!")
try {
Thread.sleep(workTime)
} catch (e: InterruptedException) {
e.printStackTrace()
}finally {
countDownLatch.countDown()
}
// println("Sub thread is stopping!")
}
}
// 主线程
private fun mainWork() {
println("Main thread start work!")
//sleep
Thread.sleep(2000L)
println("Main Thread work done!")
}
}
但是有一点有意思的现象,mainwork的“Main Thread work done!”总是在所有的子线程执行完毕后才执行,即使他的休眠时间比有的子线程时间少,这点令人不解。
三、CyclicBarrier
示例:
object CyclicBarrierTest {
@JvmStatic
fun main(args: Array<String>) {
//定义线程数
val subThreadNum = 5
//取得一个倒计时器,从5开始
val cyclicBarrier = CyclicBarrier(subThreadNum)
//依次创建5个线程,并启动
for (i in 0 until subThreadNum) {
Thread(SubThread(2000L * (i + 1), cyclicBarrier)).start()
}
mainWork()
// 等待所有的子线程结束
cyclicBarrier.await()
println("Now all thread done!")
/**
* 运行结果
* Main thread start work!
Sub thread is starting!
Sub thread is starting!
Sub thread is starting!
Sub thread is starting!
Sub thread is starting!
Main Thread work done!
Now all thread done!
*/
}
// 子线程
class SubThread(var workTime:Long,var cyclicBarrier: CyclicBarrier) : Runnable {
override fun run() {
println("Sub thread is starting!")
try {
Thread.sleep(workTime)
} catch (e: InterruptedException) {
e.printStackTrace()
}finally {
// 到达屏障
cyclicBarrier.await()
}
// println("Sub thread is stopping!")
}
}
// 主线程
private fun mainWork() {
println("Main thread start work!")
//sleep
Thread.sleep(2000L)
println("Main Thread work done!")
}
}
说明:CountDownLatch和CyclicBarrier有什么区别呢?
他们的区别:CountDownLatch只能使用一次,而CyclicBarrier方法可以使用reset()方法重置,所以CyclicBarrier方法可以处理更为复杂的业务场景。
四、Future
说明:使用并发包下面的Future模式.
Future是一个任务执行的结果, 他是一个将来时, 即一个任务执行, 立即异步返回一个Future对象, 等到任务结束的时候, 会把值返回给这个future对象里面. 我们可以使用ExecutorService接口来提交一个线程.(注意:Future.get()为一个阻塞方法)
示例:
object FutureTest {
@JvmStatic
fun main(args: Array<String>) {
// 创建一个为1的线程池
val tp = Executors.newFixedThreadPool(1)
val t = Thread(SubThread())
val future = tp.submit(t)
mainWork()
// 阻塞,等待子线程结束
future.get()
println("Now all thread done!")
// 关闭线程池
tp.shutdown()
}
// 子线程
class SubThread : Runnable {
override fun run() {
println("Sub thread is starting!")
try {
Thread.sleep(5000L)
} catch (e: InterruptedException) {
e.printStackTrace()
}
println("Sub thread is stopping!")
}
}
// 主线程
private fun mainWork() {
println("Main thread start work!")
//sleep
Thread.sleep(2000L)
println("Main Thread work done!")
}
}
总结:
以上几种方式根据业务场景和需求选用合适的同步方式
参考文章: