打印零与奇偶数

一、题目描述: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);
        }
    }

 

posted @ 2020-10-20 17:18  handsomecui  阅读(322)  评论(0编辑  收藏  举报