使用 2 个不同的类打印偶数和奇数

Posted

技术标签:

【中文标题】使用 2 个不同的类打印偶数和奇数【英文标题】:Printing Even and Odd numbers using 2 different classes 【发布时间】:2014-08-06 12:27:45 【问题描述】:

这是我在一次工作面试中提出的一个问题:

你有 2 个不同的类(实现 Runnable),比如 EvenThread 和 OddThread。顾名思义,EvenThread 只打印偶数,奇数线程只打印奇数,范围为 0-100。

class EvenThread implements Runnable 

    @Override
    public void run() 
        for (int i = 0; i <= 10; i += 2) 
            System.out.println(i);
        
    


class OddThread implements Runnable 

    @Override
    public void run() 
        for (int i = 1; i < 10; i += 2) 
            System.out.println(i);
        
    


public class EvenOdd 

    public static void main(String args[]) 
        Thread tEven = new Thread(new EvenThread());
        Thread tOdd = new Thread(new OddThread());

        tEven.start();
        tOdd.start();
    

现在我们需要强制执行一种机制,使数字按顺序打印(即 0、1、2、3、4、.... 等等)。

我在 Stack Overflow 中看到过很多类似的问题,但他们只有一个类来打印数字,并在其中调用了 2 个同步方法。

哪位高手可以推荐一下吗?

【问题讨论】:

您需要一个线程通知另一个线程打印完成,以便另一个线程可以打印并通知返回。 通知,就像 Sotirios 说的那样。我很好奇这是否可以通过将原子变量作为每个线程(锁/信号量)的一部分来实现,从而消除了对通知的需要 @Kyte 那不会留下竞争条件吗?什么会阻止您获得1 3 2 4 ... @SotiriosDelimanolis:感谢您的回复!如果我做对了,我们需要实现等待通知机制,即一旦 EvenThread 完成打印偶数,它应该通知 OddThread 打印奇数。同样,OddThread 应该等待并通知 EvenThread。但是,我没有得到关于如何实现这一点的提示。你能用示例代码解释一下吗? 必须对线程进行硬编码才能知道是否从打印开始以检查其他线程的原子变量 【参考方案1】:

这是一个带有低级等待/通知机制的丑陋示例:

public class Main 
    static boolean turn = false; // false is even, true is odd

    public static void main(String[] args) 
        Object o = new Object();
        Thread tEven = new Thread(new EvenThread(o));
        Thread tOdd = new Thread(new OddThread(o));

        tEven.start();
        tOdd.start();
    

    // TODO some inheritance with [Even/Odd]Thread

    static class EvenThread implements Runnable 
        Object o;

        EvenThread(Object o) 
            this.o = o;
        

        @Override
        public void run() 
            for (int i = 0; i <= 10; i += 2) 
                synchronized (o) 
                    try 
                        while (turn) 
                            o.wait();
                        
                    
                    catch (InterruptedException ie) 
                        ie.printStackTrace();
                    
                    finally 
                        System.out.println(i);
                        turn = !turn;
                        o.notifyAll();
                    
                
            
        
    

    static class OddThread implements Runnable 
        Object o;

        OddThread(Object o) 
            this.o = o;
        

        @Override
        public void run() 
            for (int i = 1; i < 10; i += 2) 
                synchronized (o) 
                    try 
                        while (!turn) 
                            o.wait();
                        
                    
                    catch (InterruptedException ie) 
                        ie.printStackTrace();
                    
                    finally 
                        System.out.println(i);
                        turn = !turn;
                        o.notifyAll();
                    
                
            
        
    

输出

0
1
2
3
4
5
6
7
8
9
10

【讨论】:

太棒了! 'Object o' 的共享方式是我试图实现的技巧。非常感谢! :)【参考方案2】:

不是直接回答您的问题,只是为了表明您并不总是需要锁定或同步 - memory barrier 就足够了。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class EvenAndOdd implements Runnable 

public static final int MAX_RUNTIME_SECONDS = 3;

public static void main(String[] args) 

    ExecutorService tp = Executors.newCachedThreadPool();
    AtomicInteger counter = new AtomicInteger();
    tp.execute(new EvenAndOdd(counter, true));
    //try  Thread.sleep(500);  catch (Exception ignored) 
    tp.execute(new EvenAndOdd(counter, false));
    tp.shutdown();
    boolean tpTerminated = false;
    try 
        if (tp.awaitTermination(MAX_RUNTIME_SECONDS, TimeUnit.SECONDS)) 
            tpTerminated = true;
            System.out.println("Finished.");
        
     catch (Exception e) 
        e.printStackTrace();
     finally 
        if (!tpTerminated) 
            System.out.println("Forcing shutdown.");
            tp.shutdownNow();
        
    


public static final int MAX_COUNTER = 10;

private final boolean odd;
private final AtomicInteger counter;

public EvenAndOdd(AtomicInteger counter, boolean odd) 
    this.odd = odd;
    this.counter = counter;


@Override
public void run() 

    int emptyCycleCounter = 0;
    while (true) 
        int i = counter.get();
        if (i > MAX_COUNTER) 
            break;
        
        if (i % 2 == (odd ? 1 : 0)) 
            System.out.println(i + (odd ? " odd" : " even"));
            counter.incrementAndGet();
         else 
            emptyCycleCounter++;
            Thread.yield();
        
    
    System.out.println("Finished"  + (odd ? " odd" : " even") + " with " + emptyCycleCounter + " empty cycles.");


【讨论】:

【参考方案3】:

公共类 MyLock

public MyLock(Boolean even) 
    super();
    this.even = even;


static Boolean even = null;

public synchronized void lock(boolean value) 

    while (even != value) 
        try 
                    wait();
         catch (InterruptedException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
        
    



public synchronized void unlock() 
    even = !even;
    notify();

公共类奇偶

private static final int Max_CCOUNTER = 100;
static final MyLock lock = new MyLock(true);

public static void main(String[] args) 
    // TODO Auto-generated method stub
    new Thread(() -> printEven(), "EVEN").start();
    ;
    new Thread(() -> printOdd(), "ODD").start();
    ;



static void printEven() 

    for (int i = 0; i < Max_CCOUNTER; i = i + 2) 
        lock.lock(true);
        System.out.println(i + ":" + Thread.currentThread().getName());
        lock.unlock();
    


static void printOdd() 

    for (int i = 1; i < Max_CCOUNTER; i = i + 2) 
        lock.lock(false);
        System.out.println(i + ":" + Thread.currentThread().getName());
        lock.unlock();

    

【讨论】:

以上是关于使用 2 个不同的类打印偶数和奇数的主要内容,如果未能解决你的问题,请参考以下文章

向偶数和奇数 div 添加不同的类

浏览器里怎么打印奇数页和偶数页?

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

用php打出1到100之间的偶数之和,还有奇数之和,还有定义一个变量,用for循环打出来,总共三个

Java23线程练习:奇偶数

不同的样式取决于复选框的数量是奇数还是偶数