题目
四个线程A、B、C、D向四个文件写入数据。要求A线程只写入A,B线程只写入B…… 最终达到的效果: A.txt内容为: A B C D A B C D…… B.txt内容为: B C D A B C D A…… C.txt内容为: C D A B C D A B…… D:txt内容为: D A B C D A B C……
分析
每个线程的职责:A线程只写A,B线程只写B,etc。
使用一个锁,保护共享资源即可。
怎样使4个线程按顺序写入? 很简单,一开始4个线程都处于阻塞状态,然后先唤醒A。这样A结束后,自动唤醒B,etc。 本质是线程间通信。可以使用Object的wait、notfiy,也可以使用Condition的await、signal。具体可以看看:
怎样控制结束?假设执行X次后自动结束。增加一个变量X,到达指定次数后结束。 但是有个细节点,后面的线程阻塞了,依赖前面的线程唤醒,才能结束。因此前一个线程结束的时候,要唤醒下一个线程。
public class PrintABCDBySequence {
// 执行次数
static final int RUN_COUNT = 10;
static volatile boolean stop = false;
static volatile boolean hasInit = false;
static AtomicInteger round = new AtomicInteger();
static int size = 4;
// 模拟向size个文件输出
static List<String>[] output = new ArrayList[size];
// 等待size个线程结束
static CountDownLatch latch = new CountDownLatch(size);
static ReentrantLock lock = new ReentrantLock();
static Condition waitForA = lock.newCondition();
static Condition waitForB = lock.newCondition();
static Condition waitForC = lock.newCondition();
static Condition waitForD = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < size; i++) {
output[i] = new ArrayList<>();
}
new Thread(new PrintTask("A", waitForD, waitForA)).start();
new Thread(new PrintTask("B", waitForA, waitForB)).start();
new Thread(new PrintTask("C", waitForB, waitForC)).start();
new Thread(new PrintTask("D", waitForC, waitForD)).start();
// 等待4个线程启动
Thread.sleep(500);
hasInit = true;
lock.lock();
// 先唤醒A
try {
waitForD.signal();
} finally {
lock.unlock();
}
latch.await();
for (List<String> a : output) {
System.out.println(a);
}
}
static class PrintTask implements Runnable {
private String ch;
private Condition prevCondition;
private Condition selfCondition;
public PrintTask(String ch, Condition prevCondition, Condition selfCondition) {
this.ch = ch;
this.prevCondition = prevCondition;
this.selfCondition = selfCondition;
}
@Override
public void run() {
while (true) {
lock.lock();
// 初始化,线程进入阻塞等待
if (!hasInit) {
try {
prevCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
// attention: stop的时候,要先唤醒后面的线程,否则后面的线程不能结束
if (stop) {
selfCondition.signal();
break;
}
int r = round.getAndIncrement();
for (int i = 0; i < output.length; i++) {
if (i <= r) {
output[i].add(ch);
} else {
break;
}
}
if (r >= RUN_COUNT) {
stop = true;
}
selfCondition.signal();
if (!stop) {
prevCondition.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
latch.countDown();
}
}
}