欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-04-09 19:42:56
前提 今天下班时候和同事聊天偶然听到面试题“两个线程交替打印奇数和偶数”的实现,这里做一个复盘。 复盘 场景一:线程A打印奇数,线程B打印偶数,线程A和线程B交替打印,使用对象监视器实现。 场景二:线程A打印奇数,线程B打印偶数,线程A和线程B交替打印,使用JDK提供的并发类库实现。 这两个场景中, ......

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

前提

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

复盘

场景一:线程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的一些原理,那么实现起来自然比较简单。

写在最后

  • 第一:看完点赞,感谢您的认可;
  • ...
  • 第二:随手转发,分享知识,让更多人学习到;
  • ...
  • 第三:记得点关注,每天更新的!!!
  • ...

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