题目
猴子分桃:3只猴子抢着分100只桃子,每只抢到的猴子分去剩余桃子的一半。使用多线程模拟这一过程。
分析
面向对象就是抽取模型:
第一个是猴子很简单,它有桃子数量的属性和抢桃的行为;
第二个,表面上是桃子,但其实是一堆桃子,这里我们建模为桃篮子,它有桃子数量的属性和减少一半的行为。
编码
首先定义猴子。
为了方便跟踪抢的过程我们加了一个名称属性,并且重写toString方法。
在抢桃行为里我们把桃篮子传递进去,然后不断地拿走一半加到自己的数量里,直到无桃可抢,为了更加现实我们在每次抢了之后等待1秒钟。。
用多线程模拟,那就让猴子继承线程类。
/**
* 猴子
*/
public class Monkey extends Thread {
/** 猴子名称 */
private String name;
/** 桃子数量 */
private int peachNum;
public Monkey(String name) {
this.name = name;
}
/**
* 抢桃
*
* @param peachBasket 桃篮子
*/
public void snatchPeach(PeachBasket peachBasket) {
for (;;) {
int half = peachBasket.reduceByHalf();
if (half == 0) {
break;
}
System.out.printf("%s抢到了%s个桃子!%n", name, half);
peachNum += half;
// 等待一秒
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
}
}
@Override
public String toString() {
return "Monkey{" +
"name='" + name + '\'' +
", peachNum=" + peachNum +
'}';
}
}
然后定义桃篮子。
构造时传入桃子数量。
减少一半注意线程安全和剩余奇数的情况。
/**
* 桃篮子
*/
public class PeachBasket {
/** 桃子数量 */
private int peachNum;
public PeachBasket(int peachNum) {
this.peachNum = peachNum;
}
/**
* 减少一半
*
* @return 被分走的数量
*/
public synchronized int reduceByHalf() {
if (peachNum == 1) {
peachNum = 0;
return 1;
} else if (peachNum > 1) {
int half = peachNum / 2;
peachNum -= half;
return half;
}
return 0;
}
}
最后我们模拟这个过程
public class Test {
public static void main(String[] args) throws InterruptedException {
// 桃篮子
PeachBasket peachBasket = new PeachBasket(100);
Monkey[] monkeys = new Monkey[3];
// 只是为了等待抢完输出结果
CountDownLatch countDownLatch = new CountDownLatch(monkeys.length);
for (int i = 0; i < monkeys.length; i++) {
// 创建猴子,设置线程运行时执行抢桃子的方法
monkeys[i] = new Monkey("猴子" + (i + 1)) {
@Override
public void run() {
this.snatchPeach(peachBasket);
countDownLatch.countDown();
}
};
}
// 线程启动
Arrays.stream(monkeys).forEach(Thread::start);
countDownLatch.await();
// 输出结果
Arrays.stream(monkeys).forEach(System.out::println);
}
}
运行后控制台打印如下:
猴子1抢到了50个桃子!
猴子3抢到了12个桃子!
猴子2抢到了25个桃子!
猴子3抢到了6个桃子!
猴子1抢到了3个桃子!
猴子2抢到了2个桃子!
猴子3抢到了1个桃子!
猴子1抢到了1个桃子!
Monkey{name='猴子1', peachNum=54}
Monkey{name='猴子2', peachNum=27}
Monkey{name='猴子3', peachNum=19}