无意中在一篇文章中看到一个面试题,题目内容为:现有一个统计任务,需要3个线程完成,在这三个线程完成后由一个线程完成最后的统计报告工作,写出大概代码。
其实多线程问题都可以从两个角度考虑实现,一种是java5之前的同步关键字去实现,另一种是java5之后提供的多线程新特性角度去考虑。
对于传统的同步关键字思路:四个线程都共享一个信号量,当信号量标志子线程任务都完成时,唤醒最后的统计线程;对于java5并发包的思路:java5对于该类线程协作问题提供了CountDownLatch、CyclicBarrier、Semaphore等工具类,拿过来就可以用,这里可以用CountDownLatch工具类,该工具类作用是某个任务需要等待其他几个任务完成后才能开始执行。下面给出源码:
同步关键字:
package countdownlatch.Test2;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by dingxiangyong on 2016/3/25.
*/
public class Test {
public static void main(String[] args) {
Statistic statistic = new Statistic();
/**
* 信号量
*/
Single single = new Single(3);
StatisticSubTask task1 = new StatisticSubTask(statistic, single);
StatisticSubTask task2 = new StatisticSubTask(statistic, single);
StatisticSubTask task3 = new StatisticSubTask(statistic, single);
StatisticAllTask taskAll = new StatisticAllTask(statistic, single);
ExecutorService service = Executors.newFixedThreadPool(4);
service.execute(task1);
service.execute(task2);
service.execute(task3);
service.execute(taskAll);
service.shutdown();
}
}
class StatisticSubTask implements Runnable{
private Statistic statistic;
private Single single;
public StatisticSubTask(Statistic statistic, Single single) {
this.statistic = statistic;
this.single = single;
}
@Override
public void run() {
try {
Thread.sleep(1000); //模拟统计耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (statistic) {
statistic.result.add(new Object());
}
//减少信号量
synchronized(single) {
single.countDown();
if (single.isEmpty()) {
single.notify();
}
}
}
}
class StatisticAllTask implements Runnable{
private Statistic statistic;
private Single single;
public StatisticAllTask(Statistic statistic, Single single) {
this.statistic = statistic;
this.single = single;
}
@Override
public void run() {
synchronized(single) {
//看信号量,决定是否可以继续处理
while(!single.isEmpty()) {
try {
single.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("result : " + statistic.result.size());
}
}
/**
* 统计结果
*/
class Statistic {
public List<Object> result = new ArrayList<Object>();
}
class Single {
private Integer count = 0;
public Single(Integer count) {
this.count = count;
}
public void countDown() {
this.count --;
}
public boolean isEmpty() {
return this.count == 0;
}
}
CountDownLatch实现:
package countdownlatch;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by dingxiangyong on 2016/3/25.
*/
public class Test {
public static void main(String[] args) {
/**
* 统计结果
*/
Statistic statictis = new Statistic();
/**
* 协同其他线程的锁
*/
CountDownLatch latch = new CountDownLatch(3);
StatisticSubTask task1 = new StatisticSubTask(latch, statictis);
StatisticSubTask task2 = new StatisticSubTask(latch, statictis);
StatisticSubTask task3 = new StatisticSubTask(latch, statictis);
StatisticAllTask taskAll = new StatisticAllTask(latch, statictis);
ExecutorService service = Executors.newFixedThreadPool(4);
service.execute(task1);
service.execute(task2);
service.execute(task3);
service.execute(taskAll);
service.shutdown();
}
}
class StatisticSubTask implements Runnable {
/**
* 协同其他线程的锁
*/
private CountDownLatch latch;
private Statistic statictis;
public StatisticSubTask(CountDownLatch latch, Statistic statictis) {
this.latch = latch;
this.statictis = statictis;
}
@Override
public void run() {
try {
Thread.sleep(1000); //模拟统计耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (statictis) {
statictis.result.add(new Object());
}
latch.countDown(); //通知其他线程本线程任务已完成
}
;
}
class StatisticAllTask implements Runnable {
/**
* 协同其他线程的锁
*/
private CountDownLatch latch;
private Statistic statictis;
public StatisticAllTask(CountDownLatch latch, Statistic statictis) {
this.latch = latch;
this.statictis = statictis;
}
@Override
public void run() {
try {
latch.await();//等待其他统计线程
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("result : " + statictis.result.size());
}
;
}
/**
* 统计结果
*/
class Statistic {
public List<Object> result = new ArrayList<Object>();
}
对比代码,使用新特性CountDownLatch之后代码变得优雅许多。