悲剧的一天,本来想好好整理下,准备开始干《HTTP权威指南》,结果宿舍太吵,现在开始整理关于并发的知识吧,还是要淡定。
Executor框架
基于Executor的Web服务器
public class TaskExecutorWebServer {
private static final int NTHREADS = 100;
private static final Executor executor = Executors.newFixedThreadPool(NTHREADS);
public static void main(String[] args) throws IOException, InterruptedException {
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
final Socket socket = serverSocket.accept();
Runnable task = new Runnable() {
@Override
public void run() {
// TODO
}
};
executor.execute(task);
}
}
}
public class ThreadPerTaskExecutor implements Executor {
@Override
public void execute(Runnable command) {
new Thread(command).start();
}
}
同样,还可以编写一个Executor使TaskExecutionWebServer的行为类似于单线程的行为,即以同步的方式执行每个任务,然后再返回。
public class WithinThreadExecutor implements Executor {
@Override
public void execute(Runnable command) {
command.run();
}
}
执行策略
- 在什么线程中执行任务,
- 任务按照什么顺序执行。
- 多少个任务能并发执行。
- 在队列中有多少个任务执行。
- 如果系统由于过载而需要拒绝一个任务,选择哪个任务,如何通知应用程序有任务拒绝。
- 在一个任务执行前后应该执行什么操作。
当看到下面这种形式的代码:
new Thread(runnable).start();
并且你想获得一个更灵活的执行策略时,请认真考虑使用Executor来代替Thread
线程池
- newFixedThreadPool: 创建一个固定大小的线程池
- newCachedThreadPool: 创建一个可缓存的线程池。
- newSingleThreadExecutor: 单线程执行任务,
- newScheduledThreadPool: 延时或定时执行任务,
public class LifecycleWebServer {
private final ExecutorService exec = Executors.newCachedThreadPool();
public void start() throws IOException {
ServerSocket socket = new ServerSocket(80);
while (!exec.isShutdown()) {
try {
final Socket conn = socket.accept();
exec.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
});
} catch (RejectedExecutionException e) {
if (!exec.isTerminated())
log(e);
}
}
}
public void stop() {
exec.shutdown();
}
}
CompletionService
public class Renderer {
private final ExecutorService executorService;
public Renderer(ExecutorService executorService) {
this.executorService = executorService;
}
void renderPage(CharSequence source) {
List<ImageInfo> info = scanForImageInfo(source);
CompletionService<ImageData> completionService = new ExecutorCompletionService<ImageData>(
executorService);
for (ImageInfo imageInfo : info) {
completionService.submit(new Callable<ImageData>() {
@Override
public ImageData call() throws Exception {
return imageInfo.downloadImage();
}
});
}
renderText(source);
try {
for (int t = 0, n = info.size(); t < n; t++) {
Future<ImageData> f = completionService.take();
ImageData imagData = f.get();
renderImage(imagData);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
throw launderThrowable(e.getCause());
}
}
}
ExecutorCompletionService源码中QueueingFuture继承FutureTask重写了done方法。将完成的结果保存到completionQueue(LinkedBlockingQueue),所以事例中 可以在for中使用completionService.take(),实现异步加载。
一个设定时限的广告,当超过预设的时间,将默认的广告加入到页面
Page renderWithAd() {
long endNanos = System.nanoTime() + TIME_BUDGET;
Future<Ad> f = exec.submit(new FetchAdTask());
Page page = renderPageBody();
Ad ad;
try {
long timeLeft = endNanos - System.nanoTime();
ad = f.get(timeLeft, TimeUnit.SECONDS);
} catch (ExecutionException e) {
ad = DEFAULT_AD;
} catch (Exception e) {
ad = DEFAULT_AD;
f.cancel(true);
}
page.add(ad);
return page;
}
一个不错的例子,可以用CompletionService实现 ,这里用了invokeAll方法,更加简单,可以将精力放在业务逻辑上。
class QuoteTask implements Callable<TravelQuote> {
private final TravelCompany company;
private final TravelInfo travelInfo;
...
public TravelQuote call() throws Exception {
return company.solicitQuote(travelInfo);
}
}
getRankedTravelQuotes方法对异常进行了处理 值得借鉴。。。
try {
quotes.add(f.get());
} catch (ExecutionException e) {
quotes.add(task.getFailureQuote(e.getCause()));
} catch (CancellationException e) {
quotes.add(task.getTimeoutQuote(e));
}
public List<TravelQuote> getRankedTravelQuotes(TravelInfo travelInfo,Set<TravelCompany> companies, Comparator<TravelQuote> ranking,long time, TimeUnit unit)
throws InterruptedException {
List<QuoteTask> tasks = new ArrayList<QuoteTask>();
for (TravelCompany company : companies)
tasks.add(new QuoteTask(company, travelInfo));
List<Future<TravelQuote>> futures = exec.invokeAll(tasks, time, unit);
List<TravelQuote> quotes = new ArrayList<TravelQuote>(tasks.size());
Iterator<QuoteTask> taskIter = tasks.iterator();
for (Future<TravelQuote> f : futures) {
QuoteTask task = taskIter.next();
try {
quotes.add(f.get());
} catch (ExecutionException e) {
quotes.add(task.getFailureQuote(e.getCause()));
} catch (CancellationException e) {
quotes.add(task.getTimeoutQuote(e));
}
}
Collections.sort(quotes, ranking);
return quotes;
}
Executor 框架简化了开发过程,将任务提交与执行策略解耦合,很多工具类源码值得分析,分析下设计的模式,设计的很巧妙。阅读源码很是有好处的,分析CompletionService 感觉自己设计的不足,看来设计模式没有融会贯通啊。