Android开发经验谈Android技术知识Manba陪你学Android

阿里面试题-多线程按序打印

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()
    }
}

如果有更好的建议或方法,欢迎评论区留言交流。

感谢

Java并发编程之CyclicBarrier详解

上一篇下一篇

猜你喜欢

热点阅读