问题
I have been going through the program which starts three threads and print their corresponding value such that T3
is executed first, then the T1
thread, and lastly the T2
thread is executed. Below is the program.
I just want to know if you guys could help in converting this program with respect to countdown latch, as I want to develop it using this mechanism or it can be also done through counting semaphore.
From the answer to this related question:
public class Test {
static class Printer implements Runnable {
private final int from;
private final int to;
private Thread joinThread;
Printer(int from, int to, Thread joinThread) {
this.from = from;
this.to = to;
this.joinThread = joinThread;
}
@Override
public void run() {
if(joinThread != null) {
try {
joinThread.join();
} catch (InterruptedException e) { /* ignore for test purposes */ }
}
for (int i = from; i <= to; i++) {
System.out.println(i);
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread T3 = new Thread(new Printer(10, 15, null));
Thread T1 = new Thread(new Printer(1, 5, T3));
Thread T2 = new Thread(new Printer(6, 10, T1));
T1.start();
T2.start();
T3.start();
}
}
回答1:
We consider each pair of threads Tw, Ts
such as Tw
is waiting on Ts
to commence its work. In your setup, there are 2 such pairs:
T1, T3
T2, T1
For each pair, we will create one CountDownLatch
, and provide it to each thread of the pair. Then Tw
will call await
on the latch before starting its work, and Ts
will call countDown
at the end of its own work.
Since T1
belongs to both pairs, it will receive both latches. However, in the first case, T1
is a waiting thread, and in the second, T1
is a signaling thread, therefore its code will have to be amended accordingly.
Of course you will have to remove the join
calls and related infrastructure.
Since your question title asks about latch implementation, let's just briefly say that the same semantics can be produced using a Semaphore
initialized at 0
, and where countDown
would actually be a release
of the semaphore, while await
would be an acquire
of that semaphore.
public class Test {
private CountdownLatch latch;
private Runnable runnable;
class Tw implements Runnable {
Tw(CountdownLatch l, Runnable r) {
latch = l;
runnable = r;
}
@override
public void run(){
latch.await();
runnable.run();
}
}
class Ts implements Runnable {
CountdownLatch latch;
Runnable runnable;
Ts(CountdownLatch l, Runnable r){
latch = l;
runnable = r;
}
@override
public void run(){
runnable.run();
latch.countDown();
}
}
static class Printer implements Runnable {
private final int from;
private final int to;
Printer(int from, int to) {
this.from = from;
this.to = to;
}
@Override
public void run() {
for (int i = from; i <= to; i++) {
System.out.println(i);
}
}
public static void main(String[] args) throws InterruptedException {
CountdownLatch l31 = new CountdownLatch(1), l12 = new CountdownLatch(1);
Thread T3 = new Thread(new Ts(l31, new Printer(10, 15, null)));
Thread T1 = new Thread(new Tw(l31, new Ts(l12, new Printer(1, 5, T3))));
Thread T2 = new Thread(new Tw(l12, new Printer(6, 10, T1)));
T1.start();
T2.start();
T3.start();
}
}
The proposed sample implementation uses auxiliary runnables to take care of the latching process, thus allowing us to compose each task using these runnables, instead of deriving the Printer
class for each specific case (we save at least one class).
The Semaphore
based similar implementation is left as an exercise for the reader.
来源:https://stackoverflow.com/questions/15030808/regarding-countdown-latch-implementation