使用 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 个不同的类打印偶数和奇数的主要内容,如果未能解决你的问题,请参考以下文章