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

前提

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

复盘

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

写在最后

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

原文地址:https://www.cnblogs.com/Java-no-1/p/11119746.html

时间: 2024-10-24 10:18:03

经典面试题——两个线程交替打印奇数和偶数的相关文章

经典笔试题:两个线程交替打印奇偶数

一.采用对象的wait() notify()方法实现 package com.gaopeng.programming; import java.util.concurrent.TimeUnit; /** * 经典笔试题:交替打印奇偶数 采用对象的wait() notify()方法实现 * * @author gaopeng * */ public class OddEvenThread { private static volatile Integer counter = 0; public s

两个线程交替打印奇数和偶数

public class ThreadTest { public static void main(String[] args) { Thread evenThread = new Thread(new PrintEven(),"打印奇数"); Thread oddThread = new Thread(new PrintOdd(),"打印偶数"); evenThread.start(); oddThread.start(); } } class Count{ pu

两个线程交替打印信息

看见一个关于两个线程交替打印信息的题目,题目大概是 子线程循环 10 次,接着主线程循环 100 次,接着又回到子线程循环 10 次,接着再回到主线程又循环 100 次,如此循环50次,试写出代码. 写了两个版本,一个是用了mutex,不用条件变量:另外一个是用条件变量. 第一个,不用条件变量 1 #include <stdio.h> 2 #include <string.h> 3 #include <pthread.h> 4 5 6 7 const int LOOP_

两个线程交替打印1-99

参考https://github.com/crossoverJie/JCSprout/blob/master/src/main/java/com/crossoverjie/actual/TwoThread.java从线程方面实现交替打印. public class Test { volatile boolean isEven = false; @org.junit.Test public void testfda() throws InterruptedException { Thread a

控制两个线程交替打印

package datatype; public class demo { static final Object object = new Object(); public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { for (int i = 1; i < 51; i++) { System.out.print(i); if (i

头条面试题之实现两个线程轮流打印字符串

在面试头条的时候,有一个很有意思的题目,利用两个线程交替打印一个字符串,这里主要就是对多线程中wait/notify的应用,特此记录. 对于wait()和notify()的理解,还是要从jdk官方文档中开始,在Object类方法中有: void notify() Wakes up a single thread that is waiting on this object’s monitor. 译:唤醒在此对象监视器上等待的单个线程 void notifyAll() Wakes up all t

面试题 14:调整数组顺序使奇数位于偶数前面

使用两个指针,在数组头尾相对移动: 循环结束条件:头和尾重叠活着头在尾之后 左边指针右移条件:当前数是奇数 右边指针左移条件:当前数是偶数 当且仅当左边指针是偶数,右边指针是奇数,交换两个指针的值 此题注意扩展,函数功能的重用. package offer; import java.util.Arrays; /*面试题 14:调整数组顺序使奇数位于偶数前面 题目:输入一个整数数组,实现一个函数来调整该函数数组中数字的顺序,使得所有奇数位于数组的前半部分,所有的数组位于数组的后半部分.*/ pub

面试题14:调整数组顺序使奇数位于偶数前面

// 面试题14_调整数组顺序使奇数位于偶数前面.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> using namespace std; void swap(int *begin,int *end) { int temp; temp=*begin; *begin=*end; *end=temp; } void ReorderOddEven(int *pData,unsigned int len

Java多线程通信之两个线程分别打印AB各10次

一道经典的面试题目:两个线程,分别打印AB,其中线程A打印A,线程B打印B,各打印10次,使之出现ABABABABA.. 的效果 1 package com.shangshe.path; 2 3 public class ThreadAB { 4 5 /** 6 * @param args 7 */ 8 public static void main(String[] args) { 9 10 final Print business = new Print(); 11 12 new Threa