無意中在一篇文章中看到一個面試題,題目內容爲:現有一個統計任務,需要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之後代碼變得優雅許多。