前言:上一篇文章我们使用了 ReentrantLock+Condition 实现了多线程打印 ABC,这篇文章我们来实现多线程打印数字 1~100。
(一)题目
(1)题目 1:双线程打印
使用两个线程交替打印数字。
线程 A 打印奇数(1,3,5...),线程 B 打印偶数(2,4,6...),交替输出1~100。
(2)题目 2:多线程打印
使用 N 个线程顺序循环打印从 0 至 100。
(二)解法
(1)题目 1:双线程打印
这种解法实现起来比较简单,每个线程执行的次数是固定的。
java
public class PrintNum {
private static int state = 0;
private static int times = 50;
private static Lock lock = new ReentrantLock();
private static Condition c1 = lock.newCondition();
private static Condition c2 = lock.newCondition();
private static void printN(int flag, Condition current, Condition next) {
lock.lock();
try {
for (int i = 0; i < times; i++)
{
while(state % 2 != flag) current.await();
System.out.println(Thread.currentThread().getName() + ": " + (2 * i + flag + 1));
state ++;
next.signal();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
new Thread(() -> {
printN(0, c1, c2);
}, "Thread-1").start();
new Thread(() -> {
printN(1, c2, c1);
}, "Thread-2").start();
}
}
(2)题目 2:多线程打印
与上一题不同的是,这里每个线程的执行次数不是固定的,对于每个线程需要判断 state 是否已经达到 100 了,达到就不能继续执行了。
但是因为多线程并发执行,state 的值一直在变化,所以如何保证不超输出,是一个需要注意的问题。
- 在
while判断的时候,需要加上一个条件state < 100。也就是说,如果已经达到 100 了,就不必再进入阻塞队列等待唤醒。 - 在
while执行完毕之后,按理来说就可以执行输出了,但是还需要加上一个判断,只有当state<100时才执行。这是为了防止当前线程在阻塞队列的时候有别的线程将 state 加够了。 - 当到达 100 之后,需要唤醒所有的线程。防止有的线程在等待队列中始终无法被唤醒。
java
public class PrintNum2 {
private static int state = 0;
private static int threadCnt = 7;
private static Lock lock = new ReentrantLock();
private static Condition[] conditions = new Condition[threadCnt];
static {
for (int i = 0; i < threadCnt; i++) {
conditions[i] = lock.newCondition();
}
}
private static void printN(int flag, Condition current, Condition next) {
lock.lock();
try {
while(state < 100)
{
while(state < 100 && state % threadCnt != flag) current.await(); //双重检测
if(state < 100) { //双重检测
System.out.println(Thread.currentThread().getName() + ": " + (state + 1));
state ++;
next.signal();
}
else{
for(Condition c : conditions) c.signal(); //到达100了,唤醒所有线程,防止有线程在等待队列中无法被唤醒
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
for(int i = 0; i < threadCnt; i ++)
{
int tmpI = i;
new Thread(() -> {
printN(tmpI, conditions[tmpI], conditions[(tmpI + 1) % threadCnt]);
}, "Thread-" + (i + 1)).start();
}
}
}