专栏首页upuptop的专栏经典面试题-两个线程交替打印奇数和偶数

经典面试题-两个线程交替打印奇数和偶数

前提

今天下班时候和同事聊天偶然听到面试题“两个线程交替打印奇数和偶数”的实现,这里做一个复盘。

复盘

场景一:线程A打印奇数,线程B打印偶数,线程A和线程B交替打印,使用对象监视器实现。

场景二:线程A打印奇数,线程B打印偶数,线程A和线程B交替打印,使用JDK提供的并发类库实现。

这两个场景中,场景一是一种比较古老的同步方式,本质由JVM实现;场景二是JDK1.5引入JUC包之后简化了并发编程的前提下的更简便的实现。下面针对两个场景做对应的实现。

场景一

场景一中,线程A和线程B交替打印奇数和偶数,使用对象监视器实现,通俗来说:线程A或线程B只要有一者竞争锁成功,就打印++i,通知其他线程从等待集合中释放,然后自身线程加入等待集合并且释放锁即可。

public class OddEvenPrinter {

    private final Object monitor = new Object();
    private final int limit;
    private volatile int count;

    public OddEvenPrinter(int limit, int initCount) {
        this.limit = limit;
        this.count = initCount;
    }

    public void print() {
        synchronized (monitor) {
            while (count < limit) {
                try {
                    System.out.println(String.format("线程[%s]打印数字:%d", Thread.currentThread().getName(), ++count));
                    monitor.notifyAll();
                    monitor.wait();
                } catch (InterruptedException e) {
                    //ignore
                }
            }
        }
    }

    public static void main(String[] args) throws Exception {
        OddEvenPrinter printer = new OddEvenPrinter(10, 0);
        Thread thread1 = new Thread(printer::print, "thread-1");
        Thread thread2 = new Thread(printer::print, "thread-2");
        thread1.start();
        thread2.start();
        Thread.sleep(Integer.MAX_VALUE);
    }
}

执行后的输出结果:

线程[thread-1]打印数字:1
线程[thread-2]打印数字:2
线程[thread-1]打印数字:3
线程[thread-2]打印数字:4
线程[thread-1]打印数字:5
线程[thread-2]打印数字:6
线程[thread-1]打印数字:7
线程[thread-2]打印数字:8
线程[thread-1]打印数字:9
线程[thread-2]打印数字:10

场景二

场景二中,如果需要使用JUC中提供的并发类库,可以考虑和对象监视器功能接近的可重入锁ReentrantLock。具体代码如下:

public class OddEvenPrinterEx {

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    private final int limit;
    private volatile int count;

    public OddEvenPrinterEx(int limit, int initCount) {
        this.limit = limit;
        this.count = initCount;
    }

    public void print()  {
        lock.lock();
        try {
           while (count < limit){
               System.out.println(String.format("线程[%s]打印数字:%d", Thread.currentThread().getName(), ++count));
               condition.signalAll();
               try {
                   condition.await();
               } catch (InterruptedException e) {
                   //ignore
               }
           }
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws Exception {
        OddEvenPrinterEx printer = new OddEvenPrinterEx(10, 0);
        Thread thread1 = new Thread(printer::print, "thread-1");
        Thread thread2 = new Thread(printer::print, "thread-2");
        thread1.start();
        thread2.start();
        Thread.sleep(Integer.MAX_VALUE);
    }
}

执行后的输出结果:

线程[thread-2]打印数字:1
线程[thread-1]打印数字:2
线程[thread-2]打印数字:3
线程[thread-1]打印数字:4
线程[thread-2]打印数字:5
线程[thread-1]打印数字:6
线程[thread-2]打印数字:7
线程[thread-1]打印数字:8
线程[thread-2]打印数字:9
线程[thread-1]打印数字:10

眼尖的可能看到这里是先由thread-2打印奇数,然后thread-1打印偶数,这个和同步器框架的等待队列以及同步队列的竞争有关。

小结

这个问题有很多种解决思路,但是目前笔者没想到无锁实现方案。很多现成的(参考多个博客)方案里面都是使用各种多重同步或者加锁,其实意义是不大,实际上要理解对象监视器和同步器框架AQS的一些原理,那么实现起来自然比较简单。参看笔者之前写的两篇文章:

  • 深入理解Object提供的阻塞和唤醒API
  • JUC同步器框架AbstractQueuedSynchronizer源码图文分析

本文分享自微信公众号 - 趣学程序(quxuecx),作者:点击关注 ????

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-07-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 经典面试题-两个线程交替打印奇数和偶数

    今天下班时候和同事聊天偶然听到面试题“两个线程交替打印奇数和偶数”的实现,这里做一个复盘。

    Throwable
  • 面试题-使用线程交替打印奇数偶数

    yunlgonn
  • 面试题-ReentrantLock实现两个线程交替打印

    前面了解了ReentrantLock的原理,今天来应用一下,使用ReentrantLock实现两个线程的交替打印。

    别明天就今天吧
  • 面试题-Synchronized实现两个线程交替打印

    线程之间的通信有很多种方式,使用Synchronized实现两个线程的交替打印,主要是利用Object对象的wait、notify方法,下面来看下代码:...

    别明天就今天吧
  • 面试题实战:给一个数 n,使用 Go 打印交替顺序零与奇偶数

    这是我写的第三题 LeetCode Concurrency(并发) Go 语言详解,技术比前面两题都要复杂。为了解释到我自认够清楚,写的时间多花了好几倍(1x ...

    架构师精进
  • 两个线程如何交替执行,一个输出偶数一个输出奇数?

    通过 synchronized 同步两个方法,每次只能有一个线程进入,每打印一个数,就释放锁,另一个线程进入,拿到锁,打印,唤醒另一个线程,然后挂起自己。循环反...

    Java技术栈
  • 学习使用Lock+Conditionk编写三个经典多线程例子

    在jdk5之后的高级并发包里面Lock接口可以替换原来jvm内置的锁synchronized关键字,同理使用Condition接口的await,signal,s...

    我是攻城师
  • 手撕面试题:多个线程顺序执行问题

    大家在换工作面试中,除了一些常规算法题,还会遇到各种需要手写的题目,所以打算总结出来,给大家个参考。

    海星
  • 18 个 Python 高效编程技巧,Mark!

    初识Python语言,觉得python满足了我上学时候对编程语言的所有要求。python语言的高效编程技巧让我们这些大学曾经苦逼学了四年c或者c++的人,兴奋的...

    小小詹同学
  • 18个Python高效编程技巧!

    初识Python语言,觉得python满足了我上学时候对编程语言的所有要求。python语言的高效编程技巧让我们这些大学曾经苦逼学了四年c或者c++的人,兴奋的...

    1480
  • 18 个 Python 高效编程技巧,Mark!

    初识Python语言,觉得python满足了我上学时候对编程语言的所有要求。python语言的高效编程技巧让我们这些大学曾经苦逼学了四年c或者c++的人,兴奋的...

    昱良
  • 这些技巧不知道,你说你懂Python?

    初识Python语言,觉得python满足了我上学时候对编程语言的所有要求。python语言的高效编程技巧让我们这些大学曾经苦逼学了四年c或者c++的人,兴奋的...

    智能算法
  • 18个Python高效编程技巧!

    初识Python语言,觉得python满足了我上学时候对编程语言的所有要求。python语言的高效编程技巧让我们这些大学曾经苦逼学了四年c或者c++的人,兴奋的...

    Python数据科学
  • 18 个 Python 高效编程技巧,Mark!

    初识Python语言,觉得python满足了我上学时候对编程语言的所有要求。python语言的高效编程技巧让我们这些大学曾经苦逼学了四年c或者c++的人,兴奋的...

    刀刀老高
  • 22个Python最佳编程技巧,新手越早知道越好

    这个情况如果要交换变量在c++中,肯定需要一个空变量。但是python不需要,只需一行,大家看清楚了

    一墨编程学习
  • Python初学者的17个技巧

    前段时间Jeff Atwood 推广了一个简单的编程练习叫FizzBuzz,问题引用如下:

    统计学家
  • 【每日算法Day 85】图解算法:一行代码解决约瑟夫环的变体

    给定一个从 到 排序的整数列表。首先,从左到右,从第一个数字开始,每隔一个数字进行删除,直到列表的末尾。第二步,在剩下的数字中,从右到左,从倒数第一个数字开...

    godweiyang
  • AutoResetEvent控制线程用法

    本文主要来自一道面试题,由于之前对AutoResetEvent的概念比较模糊(即使已经使用过了)。面试题题目很简洁:两个线程交替打印0~100的奇偶数。你可以先...

    Edison.Ma
  • 2014年第五届Java B组蓝桥杯省赛真题

    题目描述 小明到X山洞探险,捡到一本有破损的武功秘籍(2000多页!当然是伪造的)。他注意到:书的第10页和第11页在同一张纸上,但第11页和第12页不在同一...

    Max超

扫码关注云+社区

领取腾讯云代金券