我必须说,上面描述的关于递归调用任务和等待结束子订单任务的解决方案并不令我满意。这是我的解决方案,灵感来自Oracle原始文档中的CountDownLatch以及示例:人力资源 CountDownLatch。
在HRManagerCompact类实例的进程中,第一个常见线程具有等待两个子线程的门闩,这些子线程又分别具有等待其后续2个子线程的门闩...等等。
当然,门闩可以设置为与2不同的值(在CountDownLatch的构造函数中),可运行对象的数量也可以在迭代中建立,例如ArrayList,但它必须对应(倒计数的数量必须等于CountDownLatch构造函数中的参数)。
注意,根据限制条件:“level.get() < 2”,锁定数量呈指数增长,以及对象数量。1、2、4、8、16……和锁0、1、2、4……正如您所看到的,对于四个级别(level.get() < 4),在峰值16个线程运行时,将有15个等待线程和7个锁定。
package processes.countdownlatch.hr;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class HRManagerCompact extends Thread {
final int N = 2;
CountDownLatch countDownLatch;
CountDownLatch originCountDownLatch;
AtomicInteger level = new AtomicInteger(0);
AtomicLong order = new AtomicLong(0);
HRManagerCompact techLead1 = null;
HRManagerCompact techLead2 = null;
HRManagerCompact techLead3 = null;
public HRManagerCompact(CountDownLatch countDownLatch, String name,
AtomicInteger level, AtomicLong order){
super(name);
this.originCountDownLatch=countDownLatch;
this.level = level;
this.order = order;
}
private void doIt() {
countDownLatch = new CountDownLatch(N);
AtomicInteger leveli = new AtomicInteger(level.get() + 1);
AtomicLong orderi = new AtomicLong(Thread.currentThread().getId());
techLead1 = new HRManagerCompact(countDownLatch, "first", leveli, orderi);
techLead2 = new HRManagerCompact(countDownLatch, "second", leveli, orderi);
techLead1.start();
techLead2.start();
try {
synchronized (Thread.currentThread()) {
System.out.println("*** HR Manager waiting for recruitment to complete... " + level + ", " + order + ", " + orderi);
countDownLatch.await();
}
System.out.println("*** Distribute Offer Letter, it means finished. " + level + ", " + order + ", " + orderi);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + ": working... " + level + ", " + order + ", " + Thread.currentThread().getId());
Thread.sleep(10*level.intValue());
if (level.get() < 2) doIt();
Thread.yield();
}
catch (Exception e) {
e.printStackTrace();
}
System.out.println("--- " +Thread.currentThread().getName() + ": recruted " + level + ", " + order + ", " + Thread.currentThread().getId());
originCountDownLatch.countDown();
}
public static void main(String args[]){
AtomicInteger levelzero = new AtomicInteger(0);
HRManagerCompact hr = new HRManagerCompact(null, "zero", levelzero, new AtomicLong(levelzero.longValue()));
hr.doIt();
}
}
可能的注释输出(具有一定概率):
first: working... 1, 1, 10 // thread 1, first daughter's task (10)
second: working... 1, 1, 11 // thread 1, second daughter's task (11)
first: working... 2, 10, 12 // thread 10, first daughter's task (12)
first: working... 2, 11, 14 // thread 11, first daughter's task (14)
second: working... 2, 11, 15 // thread 11, second daughter's task (15)
second: working... 2, 10, 13 // thread 10, second daughter's task (13)
--- first: recruted 2, 10, 12 // finished 12
--- first: recruted 2, 11, 14 // finished 14
--- second: recruted 2, 10, 13 // finished 13 (now can be opened latch 10)
--- second: recruted 2, 11, 15 // finished 15 (now can be opened latch 11)
*** HR Manager waiting for recruitment to complete... 0, 0, 1
*** HR Manager waiting for recruitment to complete... 1, 1, 10
*** Distribute Offer Letter, it means finished. 1, 1, 10 // latch on 10 opened
--- first: recruted 1, 1, 10 // finished 10
*** HR Manager waiting for recruitment to complete... 1, 1, 11
*** Distribute Offer Letter, it means finished. 1, 1, 11 // latch on 11 opened
--- second: recruted 1, 1, 11 // finished 11 (now can be opened latch 1)
*** Distribute Offer Letter, it means finished. 0, 0, 1 // latch on 1 opened
doWork
中完成,因为下面的phaser没有arriveAndAwaitAdvance
,所以recursiveRunnable
需要到达(它确实到达了)。同时请注意,可运行的register
在执行到ExecutorService之前就已经注册了。 - John VintawaitAdvance
方法之前,屏障处于第0阶段且已注册的参与方为1。如果shouldBeRecursive
为false,则可运行对象将到达并触发屏障。如果不是false,则屏障将有2个已注册的参与方和1个已到达的参与方(直到可运行对象也到达并触发屏障)。 - John VintshouldBeRecursive
为 false,则该方(线程)到达时未注册,对吗?Oracle 文档说:未注册的方调用此方法(arrive, @depthofreality)是使用错误。但是,如果在此 phaser 上进行某些后续操作,则可能会导致 IllegalStateException 错误。 - depthofreality