今天看到一篇文章,是关于java中如何等待所有线程都执行结束,文章总结得很好,原文如下http://software.intel.com/zh-cn/blogs/2013/10/15/java-countdownlatchcyclicbarrier/?utm_campaign=优快云&utm_source=intel.youkuaiyun.com&utm_medium=Link&utm_content=others-%20Java
看过之后在想java中有很大的灵活性,应该有更多的方式可以做这件事。
这个事情的场景是这样的:许多线程并行的计算一堆问题,然后每个计算存在一个队列,在主线程要等待所有计算结果完成后排序并展示出来。这样的问题其实很常见。
1. 使用join。这种方式其实并不是那么的优雅,将所有线程启动完之后还需要将所有线程都join,但是每次join都会阻塞,直到被join线程完成,很可能所有被阻塞线程已经完事了,主线程还在不断地join,貌似有点浪费,而且两个循环也不太好看。
@Test
public void testThreadSync1() {
final Vector<Integer> list = new Vector<Integer>();
Thread[] threads = new Thread[TEST_THREAD_COUNT];
try {
for (int i = 0; i < TEST_THREAD_COUNT; i++) {
final int num = i;
threads[i] = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(random.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(num);
System.out.print(num + " add.\t");
}
});
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
threads[i].join();
System.out.print(i + " end.\t");
}
} catch (InterruptedException ie) {
ie.printStackTrace();
}
printSortedResult(list);
}
9 add. 7 add. 3 add. 5 add. 4 add. 1 add. 0 add. 0 end. 1 end. 8 add. 2 add. 2 end. 3 end. 4 end. 5 end. 6 add. 6 end. 7 end. 8 end. 9 end.
before sort
9 7 3 5 4 1 0 8 2 6
after sort
0 1 2 3 4 5 6 7 8 9
2. 使用wait/notifyAll,这个方式其实跟上面是类似的,只是比较底层些吧(join实际上也是wait)。
@Test
public void testThreadSync2() throws IOException, InterruptedException {
final Object waitObject = new Object();
final AtomicInteger count = new AtomicInteger(TEST_THREAD_COUNT);
final Vector<Integer> list = new Vector<Integer>();
Thread[] threads = new Thread[TEST_THREAD_COUNT];
for (int i = 0; i < TEST_THREAD_COUNT; i++) {
final int num = i;
threads[i] = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(random.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(num);
System.out.print(num + " add.\t");
synchronized (waitObject) {
int cnt = count.decrementAndGet();
if (cnt == 0) {
waitObject.notifyAll();
}
}
}
});
threads[i].start();
}
synchronized (waitObject) {
while (count.get() != 0) {
waitObject.wait();
}
}
printSortedResult(list);
}
3. 使用CountDownLatch,这其实是最优雅的写法了,每个线程完成后都去将计数器减一,最后完成时再来唤醒。
@Test
public void testThreadSync3() {
final Vector<Integer> list = new Vector<Integer>();
Thread[] threads = new Thread[TEST_THREAD_COUNT];
final CountDownLatch latch = new CountDownLatch(TEST_THREAD_COUNT);
for (int i = 0; i < TEST_THREAD_COUNT; i++) {
final int num = i;
threads[i] = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(random.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(num);
System.out.print(num + " add.\t");
latch.countDown();
}
});
threads[i].start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
printSortedResult(list);
}
4. 使用CyclicBarrier。这里其实类似上面,这个berrier只是在等待完成后自动调用传入CyclicBarrier的Runnable。
@Test
public void testThreadSync4() throws IOException {
final Vector<Integer> list = new Vector<Integer>();
Thread[] threads = new Thread[TEST_THREAD_COUNT];
final CyclicBarrier barrier = new CyclicBarrier(TEST_THREAD_COUNT,
new Runnable() {
public void run() {
printSortedResult(list);
}
});
for (int i = 0; i < TEST_THREAD_COUNT; i++) {
final int num = i;
threads[i] = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(random.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(num);
System.out.print(num + " add.\t");
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
});
threads[i].start();
}
System.in.read();
}
实际上这些方法也是跟那篇文章说的类似,不过都写出来可以稍微参考下。而且我相信还有更多其它方式。