打印零与奇偶数
一、题目描述:https://leetcode-cn.com/problems/print-zero-even-odd
相同的一个 ZeroEvenOdd 类实例将会传递给三个不同的线程:
线程 A 将调用 zero(),它只输出 0 。
线程 B 将调用 even(),它只输出偶数。
线程 C 将调用 odd(),它只输出奇数。
每个线程都有一个 printNumber 方法来输出一个整数。
请修改给出的代码以输出整数序列 010203040506... ,
其中序列的长度必须为 2n。
输入:n = 2
输出:"0102"
说明:三条线程异步执行,其中一个调用 zero(),另一个线程调用 even(),
最后一个线程调用odd()。正确的输出为 "0102"。
题解:3个线程,打印0的分别与奇偶线程互斥,奇偶线程互斥,多种解法。
解法一:不使用锁,使用两个全局标记线程输出状态,当不满足线程输出时让出时间片。
import java.util.function.IntConsumer; /** 打印零与奇偶数 相同的一个 ZeroEvenOdd 类实例将会传递给三个不同的线程: 线程 A 将调用 zero(),它只输出 0 。 线程 B 将调用 even(),它只输出偶数。 线程 C 将调用 odd(),它只输出奇数。 每个线程都有一个 printNumber 方法来输出一个整数。 请修改给出的代码以输出整数序列 010203040506... , 其中序列的长度必须为 2n。 输入:n = 2 输出:"0102" 说明:三条线程异步执行,其中一个调用 zero(),另一个线程调用 even(), 最后一个线程调用odd()。正确的输出为 "0102"。 https://leetcode-cn.com/problems/print-zero-even-odd * @author jy.cui * @version 1.0 * @date 2020/10/15 16:27 */ class ZeroEvenOdd { private int n; private volatile boolean flag1 = true; private volatile boolean flag2 = false; public ZeroEvenOdd(int n) { this.n = n; } // printNumber.accept(x) outputs "x", where x is an integer. public void zero(IntConsumer printNumber) throws InterruptedException { for(int i = 1; i <= n; i++){ while (!flag1){ Thread.yield(); } printNumber.accept(0); flag1 = false; } } public void even(IntConsumer printNumber) throws InterruptedException { for(int i = 1; i <= n; i++){ if((i & 1) == 0){ while (!flag2 || flag1){ Thread.yield(); } printNumber.accept(i); flag2 = false; flag1 = true; } } } public void odd(IntConsumer printNumber) throws InterruptedException { for(int i = 1; i <= n; i++){ if((i & 1) == 1){ while (flag2 || flag1){ Thread.yield(); } printNumber.accept(i); flag2 = true; flag1 = true; } } } }
解法二:信号量Semaphore,思路,三个信号量,分别阻塞,等待其他线程的唤醒,奇偶线程输出完毕唤醒zero线程,zero根据输出的次数,判断唤醒奇偶线程。
Semaphore用法:
void acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
void release():释放一个许可,将其返回给信号量。
int availablePermits():返回此信号量中当前可用的许可数。
boolean hasQueuedThreads():查询是否有线程正在等待获取。
class ZeroEvenOdd { private int n; private Semaphore zero = new Semaphore(0); private Semaphore odd = new Semaphore(0); private Semaphore even = new Semaphore(0); public ZeroEvenOdd(int n) { this.n = n; } // printNumber.accept(x) outputs "x", where x is an integer. public void zero(IntConsumer printNumber) throws InterruptedException { for(int i = 1; i <= n; i++){ if (i != 1){ zero.acquire(); } printNumber.accept(0); if((i & 1) == 0){ even.release(); }else { odd.release(); } } } public void even(IntConsumer printNumber) throws InterruptedException { for(int i = 1; i <= n; i++){ if((i & 1) == 0){ even.acquire(); printNumber.accept(i); zero.release(); } } } public void odd(IntConsumer printNumber) throws InterruptedException { for(int i = 1; i <= n; i++){ if((i & 1) == 1){ odd.acquire(); printNumber.accept(i); zero.release(); } } } }
测试线程:
public static void main(String[] args) throws InterruptedException { ZeroEvenOdd foo = new ZeroEvenOdd(5); new Thread(()->{ try { foo.zero(new A()); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); new Thread(()->{ try { foo.odd(new A()); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); Thread threadC = new Thread(() -> { try { foo.even(new A()); } catch (InterruptedException e) { e.printStackTrace(); } }); threadC.start(); threadC.join(); } static class A implements IntConsumer { @Override public void accept(int value) { System.out.println(value); } }