使用Java中的两个线程打印奇数和偶数
在本文中,我们将介绍如何使用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实例的许可。