阿里面试题-多线程按序打印
2020-05-28 本文已影响0人
唠嗑008
背景
有朋友最近参加了阿里的面试,被问了一道线程同步的问题。偷偷跟你们说一下,阿里一面的最后都会问一道算法题,难度,LeetCode Easy级别。
题目
有一个Runnable任务,里面就2条语句,先打印"Hello",再打印"wold",现在创建5个线程去执行Runnable,要求先打印5个"Hello",再打印5个"wold"。
很明显,这是一道线程同步问题,考察了线程生命周期,线程相关基础方法,sleep()、awit()等,线程锁这些知识。下面用多种方案来实现。
一、简单粗暴法
class MyRunnable(var tName: String) : Runnable {
override fun run() {
func1()
}
/**
* 暴力解法,并发执行,在中间休眠
*/
private fun func1() {
println("$tName hello")
Thread.sleep(2000)
println("$tName wold")
}
}
fun main() {
for (index in 1..5) {
Thread(MyRunnable("Thread $index")).start()
}
}
二、使用synchronized同步锁
每次线程打印“hello”先加锁,然后每次线程打印“hello”之后计数器加1,前4个线程打印“hello”之后会调用wait()
来阻塞线程,等到第5个线程打印“hello”之后再唤醒所有线程。注意:kotlin没有Object的对象,kotlin所有类的父类是Any,但是Any没有实现wait()、notify()方法,所以需要创建一个java的Object对象。
private var count = 0
private var lock = Object()
class MyRunnable(var tName: String) : Runnable {
override fun run() {
printHello()
println("$tName wold")
}
private fun printHello() {
synchronized(lock) {
println("$tName hello")
if (++count < 5) {
lock.wait()
} else {
lock.notifyAll()
}
}
}
}
fun main() {
for (index in 1..5) {
Thread(MyRunnable("Thread $index")).start()
}
}
三、CyclicBarrier
利用了juc并发包下的CyclicBarrier类,它里面有个方法await()
可以让线程执行到这里就等待其他线程执行,等所有线程都到达后,会释放锁,放行所有线程。CyclicBarrier内部使用了ReentrantLock锁,其实自己也可以利用ReentrantLock来实现。
private var cyclicBarrier = CyclicBarrier(5)
class MyRunnable(var tName: String) : Runnable {
override fun run() {
cyclicBarrier()
}
/**
* CyclicBarrier可以使一定数量的线程反复地在栅栏位置处汇集。当线程到达栅栏位置时将调用await方法,
* 这个方法将阻塞直到所有线程都到达栅栏位置。如果所有线程都到达栅栏位置,那么栅栏将打开,此时所有的线程都将被释放,
* 而栅栏将被重置以便下次使用。
*/
private fun cyclicBarrier() {
println("$tName hello")
// 线程打印完hello后都在这里等待
cyclicBarrier.await()
//所有线程都到达后,会释放锁,放行所有线程
println("$tName wold")
}
}
fun main() {
for (index in 1..5) {
Thread(MyRunnable("Thread $index")).start()
}
}
四、CountDownLatch
CountDownLatch也是juc并发包下的一个工具类,它允许一个或多个线程一直等待直到其他线程执行完毕才开始执行。用给定的计数初始化CountDownLatch,其含义是要被等待执行完的线程个数。每次调用ountDown()
,计数减1,执行await()
函数会阻塞线程的执行,直到计数为0。
private var countDownLatch = CountDownLatch(5)
class MyRunnable(var tName: String) : Runnable {
override fun run() {
countDownLatch()
}
/**
* CountDownLatch是一个同步辅助类,它允许一个或多个线程一直等待直到其他线程执行完毕才开始执行。
* 用给定的计数初始化CountDownLatch,其含义是要被等待执行完的线程个数。
* 每次调用CountDown(),计数减1,主程序执行到await()函数会阻塞等待线程的执行,直到计数为0
*
* 原理:计数器通过使用锁(共享锁、排它锁)实现
*/
private fun countDownLatch() {
try {
println("$tName hello")
} finally {
//计数器减1,初始值为5
countDownLatch.countDown()
if (countDownLatch.count > 0) {
//只要计算器>0,就阻塞线程
countDownLatch.await()
}
}
println("$tName wold")
}
}
fun main() {
for (index in 1..5) {
Thread(MyRunnable("Thread $index")).start()
}
}
如果有更好的建议或方法,欢迎评论区留言交流。