使用Java中的两个线程打印奇数和偶数

时间:2020-01-09 10:35:35  来源:igfitidea点击:

在本文中,我们将介绍如何使用Java中的两个线程来打印奇数和偶数。这是一个经常问到的重要的Java多线程面试问题。

由于使用两个线程来交替打印奇数和偶数,因此该程序还显示了如何同步线程以及线程间通信的工作方式。

使用两个线程打印奇数和偶数

我们可以使用wait和notify方法编写一个Java程序来打印奇数和偶数。参见示例。

我们还可以使用Semaphore编写Java程序,Semaphore是java.util.concurrent包中的同步帮助。参见示例。

使用wait()和notify()方法

在Java程序中,有两个Runnable任务,一个用于打印偶数,另一个用于打印奇数。创建了两个线程来运行这些任务,并且使用等待通知来完成线程间通信。
还有一个带有方法printEven()和printOdd()的Printer类,该类的实例在线程之间共享。

class PrintEvenTask implements Runnable{
  Printer printer;
  int max;
  PrintEvenTask(Printer printer, int max){
    this.printer = printer;
    this.max = max;
  }
  @Override
  public void run() {
    for(int i = 2; i <= max; i+=2){		
      printer.printEven(i);
    }   
  }
}

class PrintOddTask implements Runnable{
  Printer printer;
  int max;
  PrintOddTask(Printer printer, int max){
    this.printer = printer;
    this.max = max;
  }
  @Override
  public void run() {
    for(int i = 1; i <= max; i+=2){
      printer.printOdd(i);
    }   
  }
}

public class Printer {
  boolean evenFlag = false;
  //Prints even numbers 
  public void printEven(int num){
    synchronized (this) {
      while(!evenFlag){
        try {
          wait();
        } catch (InterruptedException e) {
          System.out.println("Thread Interrupted" + e.getMessage());
        }
      }
      System.out.println(Thread.currentThread().getName() + " - " + num);
      evenFlag = false;
      // notify thread waiting for this object's lock
      notify();
    }
  }
	
  //Prints odd numbers
  public void printOdd(int num){
    synchronized (this) {
      while(evenFlag){
        try {
          //make thread to wait
          wait();
        } catch (InterruptedException e) {
          System.out.println("Thread Interrupted" + e.getMessage());
        }
      }
      System.out.println(Thread.currentThread().getName() + " - " + num);
      evenFlag = true;
      // notify thread waiting for this object's lock
      notify();
    }
  }
  public static void main(String[] args) {
    Printer printer = new Printer();
    // creating two threads
    Thread t1 = new Thread(new PrintOddTask(printer, 10), "Odd");
    Thread t2 = new Thread(new PrintEvenTask(printer, 10), "Even");
    t1.start();
    t2.start();
  }
}

输出:

Odd - 1
Even - 2
Odd - 3
Even - 4
Odd - 5
Even - 6
Odd - 7
Even - 8
Odd - 9
Even – 10

一旦线程启动并开始执行其Runnable任务的run()方法,就会调用printEven()和printOdd()方法。根据布尔标志,其中一个线程进入等待状态,其他线程输出数字,并使用notify()方法通知另一个线程。

使用信号量

Java提供的信号量实现是一个计数信号量,其中信号量通过许可进行初始化。关键部分只能在获得许可后才能执行,并且可以在执行后释放。线程被阻塞,直到获得许可为止。

为了使用两个线程打印奇数和偶数,使用了两个信号量,一个信号量初始化为一个许可,另一个信号量初始化为零。具有一个许可的信号量用于打印奇数,而另一个具有信号量的信号用于打印偶数,因为它最初具有零许可,可确保不首先打印偶数。

class PrintEvenTask implements Runnable{
  Printer printer;
  int max;
  PrintEvenTask(Printer printer, int max){
    this.printer = printer;
    this.max = max;
  }
  @Override
  public void run() {
    for(int i = 2; i <= max; i+=2){		
      printer.printEven(i);
    }   
  }
}

class PrintOddTask implements Runnable{
  Printer printer;
  int max;
  PrintOddTask(Printer printer, int max){
    this.printer = printer;
    this.max = max;
  }
  @Override
  public void run() {
    for(int i = 1; i <= max; i+=2){
      printer.printOdd(i);
    }   
  }
}

public class Printer {
  boolean evenFlag = false;
  Semaphore semaphoreEven = new Semaphore(0);
  Semaphore semaphoreOdd = new Semaphore(1);
  //Prints even numbers 
  public void printEven(int num){			
    try {
      semaphoreEven.acquire();
    } catch (InterruptedException e) {
      System.out.println("Thread Interrupted" + e.getMessage());
    }		
    System.out. println(Thread.currentThread().getName() + " - " + num);
    semaphoreOdd.release();
  }
	
  //Prints odd numbers
  public void printOdd(int num){
    try {
      semaphoreOdd.acquire();
    } catch (InterruptedException e) {
      System.out.println("Thread Interrupted" + e.getMessage());
    }		
    System.out. println(Thread.currentThread().getName() + " - " + num);
    semaphoreEven.release();
  }
  public static void main(String[] args) {
    Printer printer = new Printer();
    // creating two threads
    Thread t1 = new Thread(new PrintOddTask(printer, 10), "Odd");
    Thread t2 = new Thread(new PrintEvenTask(printer, 10), "Even");
    t1.start();
    t2.start();
  }
}

输出:

Odd - 1
Even - 2
Odd - 3
Even - 4
Odd - 5
Even - 6
Odd - 7
Even - 8
Odd - 9
Even - 10

一旦线程启动并执行其Runnable任务的run()方法,则调用printEven()和printOdd()方法。由于semaphoreOdd实例是使用一个许可初始化的,因此它可以获取并运行代码,因为semaphoreEven实例具有0许可,因此另一个线程被阻止。
当semaphoreEven.release();时从printOdd()方法调用该方法,该方法将semaphoreEven的允许增量为1,然后可以在printEven()方法中获取它。相同方式semaphoreOdd.release();在printEven()方法中调用方法,以释放已获取的semaphoreOdd实例的许可。