2016年面试某公司,一上来让我先写段代码。题目是一个长度是N的整型数组,开启N个线程让数组每个元素加上一个随机数,然后再求出这个数组的所有元素之和。
这道题考察线程之间的同步,主线程等待其他线程完成工作之后,再做自己的工作,可用CountDownLatch。
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
final static int[] array = new int[10];
static class Worker implements Runnable {
private CountDownLatch latch;
private int index;
public Worker(CountDownLatch latch, int index) {
this.latch = latch;
this.index = index;
}
@Override
public void run() {
array[index] += ThreadLocalRandom.current().nextInt();
latch.countDown();
}
}
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(new Worker(latch, i)).start();
}
latch.await(); // 等待
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += array[i];
}
System.out.println(sum);
}
这段代码简洁明了,但还是有需要留心的地方。比如static的使用,static类只能访问static修饰的变量。再比如,公共访问的变量没有必要传入到每个线程里,用final修饰即可。
我当时用的是以下方法来判断,这是不准确的:
public static int activeCount() {
return currentThread().getThreadGroup().activeCount();
}
还可以使用java.lang.Thread#join()
List<Thread> threads = new ArrayList<Thread>(N);
for (int i = 0; i < N; i++) {
Thread thread = new Thread(new Worker(i));
threads.add(thread);
thread.start();
}
// wait until done
for (Thread thread : threads)
thread.join();
}
thread.join()不能写在第一个循环里,否则主线程会在这一行阻塞,每启动一个线程会等待它结束,再会去启动下一个线程。
还可以使用线程池:
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executorService.submit(new Worker(i));
}
executorService.shutdown(); // 禁止提交任务,但已提交的会完成
while (!executorService.awaitTermination(1, TimeUnit.SECONDS)) {
System.out.println("线程池尚未关闭");
}
awaitTermination方法:接收人timeout和TimeUnit两个参数,用于设定超时时间及单位。当等待超过设定时间时,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。一般情况下会和shutdown方法组合使用。
最后看一段JDK文档中的代码:
class Driver { // ...
void main() throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(N);
for (int i = 0; i < N; ++i) // create and start threads
new Thread(new Worker(startSignal, doneSignal)).start();
doSomethingElse(); // don't let run yet
startSignal.countDown(); // let all threads proceed
doSomethingElse();
doneSignal.await(); // wait for all to finish
}
}
class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
public void run() {
try {
startSignal.await();
doWork();
doneSignal.countDown();
} catch (InterruptedException ex) {} // return;
}
void doWork() { ... }
}