任务执行
应用程序大多围绕“任务执行”构造
- 任务通常是一些抽象且离散的工作单元
- 应用程序的工作分解到多个任务中简化程序的组织结构
- 提供一种自然的事物边界来优化错误恢复过程
- 提供一种自然的并行工作结构来提升并发性
在线程中执行任务
1.找出清晰的任务边界 - 理想状况下每个线程相互独立,不依赖其他任务的状态、结构或边界效应 2. 明确任务的执行策略
- 负载均衡 3.以独立的客户请求为边界。
- 如:Web服务器、邮件服务器、文件服务器、EJB容器以及数据库服务器等通过网络接受客户的连接请求。
- 将独立的请求作为任务边界,既可以实现任务的独立性,又可以实现合理的任务规模
小结
- 任务处理过程从主线程中分离出来,使得主循环能够更快地重新等待下一个到来的连接。这使得程序在完成前面的请求之前可以接受新的请求,从而提高响应性。
- 任务可以并行处理
- 任务处理代码必须线程安全
无限制创建线程的不足
在生产环境中,“为每个任务分配一个线程”这种方法存在一些缺陷,尤其是当需要创建大量线程时:
- 线程生命周期的开销非常高。线程创建和销毁都需要时间,延迟处理的请求并且需要JVM和操作系统提供一些辅助操作。会消耗大量的计算资源。
- 资源消耗。活跃的线程会消耗系统资源,尤其是内存。
- 稳定性。可创建线程的数量上存在一个限制,其影响因素包括JVM的启动参数、Thread构造函数中请求栈大小,以及底层操作系统对线程的限制等。破坏了限制很可能抛出
OutOfMemoryError异常。
Executor框架
Executor接口:
public interface Executor {
void execute (Runnable command);
}
使用示例
其基于生产者——消费者模式,如下是一个简单的基于Executor的Web服务器示例:
class TaskExecutionWebServer {
private static final int NTHREADS = 100;
private static final Executor exec = Executors.newFixedThreadPool(NTHREADS);
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocker(80);
while(true) {
final Socket connection = socket.accept();
Runnable task = new Runnable() {
public void run() {
handleRequest(connection);
}
};
exec.execute(task);
}
}
}
通常Executor的配置是一次性的,因此在部署阶段可以完成,而提交任务的代码却会不断地扩散到整个程序中,增加了修改的难度。
为每一个请求启动一个新线程的Executor:
public class ThreadPerTaskExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
};
}
在调用线程中以同步方式执行所有任务的Executor:
public class WithinThreadExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}
执行策略
- 在什么线程中执行任务?
- 按照什么顺序? FIFO/LIFO/优先级
- 多个并发任务
- 队列的size
- 过载的拒绝策略
- 如何通知应用程序有任务被拒绝?
- 执行前后有哪些动作?
每当看到new Thread(runnable).start()并希望获得一种灵活的执行策略时,考虑使用Executor来代替Thread。
线程池
newFixedThreadPool提交一个任务创建一个线程,直到到达线程池的最大数量,此时线程池规模不再变化。若某个线程发生异常而结束则补充一个新线程。newCachedThreadPool可缓存的线程池,超出规模则回收空闲线程,需求增加就新建新线程,规模没有任何限制newSingleThreadPool单线程的ExecutornewScheduledThreadPool
使用Executor可以实现调优、管理、监视、记录日志、错误报告和其他功能,如果不使用任务执行框架,那么增加这些功能比较困难。
生命周期
运行、关闭和已终止
ExecutorService的生命周期管理办法:
public interface ExecutorService extends Executor {
void shutdown();//不接收新任务
List<Runnable> shutdownNow();//粗暴取消所有运行中任务
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
//其他用于任务提交的便利方法
}
ExecutorService关闭后提交的任务将由“拒绝执行处理器”,它会抛弃任务或者使得execute方法抛出一个未检查的RejectedExecutionException。
支持关闭操作的Web服务器
class LifecycleWebServer {
private final ExecutorService exec = ...;
public void start() throw IOException {
ServerSocket socket = new ServerSocket(80);
while (!exec.isShutdown()) {
try {
final Socket conn = socket.accept();
exec.execute(new Runnable() {
public void run() { handleRequest(conn);}
});
} catch (RejectedExecutionException e) {
if(!exec.isShutdown())
log("task submission rejected", e);
}
}
}
public void stop() {exec.shutdown();}
void handleRequest(Socket connection) {
Request req = readRequext(connection);
if(isShutdownRequest(req))
stop();
else
dispatchRequest(req);
}
}
找出可利用的并行性
如I/O操作时解放CPU,需要异步返回结果的线程。
Callable与Future接口
public interface Callable<V> {
V call() throws Exception;
}
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException,
CancellationException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException,
CancellationException, TimeoutException;
}
ThreadPoolExecutor中的默认实现
protected <T> RunnableFuture<T> newTaskFor(Callable<T> task) {
return new FutureTask<T>(task);
}
CompletonService与BlockingQueue
CompletonService将BlockingQueue与Executor融合
可以将Callable任务交给它,然后使用类似队列操作的take和poll等方法获得已完成结果,并将结果封装为Future。
private class QueueingFuture<V> extends FutureTask<V> {
QueueingFuture(Callable<V> c) {
super(c);
}
QueueingFuture(Runnable t, V r) {
super(t, r);
}
protected void done() {
completionQueue.add(this);
}
}
使用CompletonService实现页面渲染器
public class Renderer {
private final ExecutorService executor;
Renderer(ExecutorService executor) { this.executor = executor;}
void renderPage(CharSequence source) {
List<ImageInfo> info = scanForImageInfo(source);
CompletionService<ImageData> completionService =
new ExecutorCompletionService<ImageData> (executor);
for(final ImageInfo imageInfo : info)
completionService.submit(new Callable<ImageData>(){
public ImageData call() {
return imageInfo.downloadImage();
}
});
renderText(source);
try {
for( int t = 0, n = info.size(); t < n; t++) {
Future<imageData> f = completionService.take();
ImageData imageData = f.get();
renderImage(imageData);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
throw launderThrowable(e.getCause());
}
}
}
为任务设置时限
Future.get超时的应用
- 在指定时间内获取广告信息
Page rendeerPageWithAd() throw InterruptedException {
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, NANOSECONDS);
} catch (ExecutionException e) {
ad = DEFAULT_AD;
} catch (TimeoutException e) {
ad = DEFAULT_AD;
f.cancel(true);
}
page.setAd(ad);
return page;
}
- 旅行预订门户网站
private class QueueTask implements Callable<TravelQuote> {
private final TravelCompany company;
private final TravelInfo travelInfo;
...
public TravelQueue call() throws Exception (
TravelInfo travelInfo, Set<TravelCompany> companier,
Comparator<TravelQueue> ranking, long time, TimeUnit unit)
throws InterruptedException {
List<QuoteTask> tasks = new ArrayList<QuoteTask>();
for(TravelCompany company : companier)
tasks.add(new QuoteTask(company, travelInfo));
List<Future<TravelQuote>> futures = exec.invokeAll(tasks, time, unit);
List<TravelQueue> quotes = new ArrayList<TravelQuote>(tasks.size());
Interator<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;
}
}
}
任务取消
- 用户请求取消
- 有时间限制的请求
- 应用程序事件
- 错误
- 关闭
利用volatile类型的域保存取消状态
@ThreadSafe
public class PrimeGenerator implements Runnable {
@GuardedBy("this")
private final List<BigInteger> primes = new ArrayList<BigInteger>();
private volatile boolean cancelled;
public void run() {
BigInteger P = BigInteger.ONE;
while (!canceled) {
p = p.nextProbablePrime();
synchronized(this) {
primes.add(p);
}
}
}
public void cancel() {
cancelled = true;
}
public synchronized List<BigInteger> get () {
return new ArrayList<BigInteger>(primes);
}
}
- 关于中断
在
Java的API或语言规范中,并没有将中断与任何取消语义关联起来,但实际上,如果在取消之外的其他操作中使用中断,那么都是不合适的,并且很难支撑起更大的应用。
调用interrupt并不意味着立即停止目标线程正在进行的工作,而只是传传递了请求中的新消息。
通常中断是实现取消最合理的方式。
通过中断来取消的示例:
class PrimeProducer extends Thread {
private final BlockingQueue<BigInteger> queue;
PrimeProducer(BlockingQueue<BigInteger> queue) {
this.queue = queue;
}
public void run() {
try {
BigInteger p = BigInteger.ONE;
while (!Thread.currentThread().isInterrupted())
queue.put(p = p.nextProbablePrime());
} catch (InterruptedException consumed) {
/**允许线程退出 */
}
}
public void cancel() {interrupt();}
}
由于每个线程拥有各自的中断策略,因此除非你知道中断对该线程的意义,否则就不应该中断这个线程
批评者曾嘲笑Java的中断功能,因为它没有给出抢占式中断机制,并强迫开发人员必须处理InterruptedException。然而,通过推迟中断请求处理,开发人员能制定更灵活的中断策略,从而保持应用程序达到响应性和健壮性的平衡。
响应中断
- 传递异常,从而使你的方法成为可中断的阻塞方法
- 恢复中断状态,从而使用栈上的上层代码能够对其进行处理
只有实现了线程中断策略的代码才能屏蔽中断请求,在常规任务和库代码中都不应该屏蔽中断请求。
计时运行
join的不足:无法知道执行控制是因为线程正常退出而返回还是因为join超时而返回。Future.get():抛出InterruptedException或TimeoutException时,如果不再需要结果就调用Future.cancel取消任务。
处理不可中断的阻塞
线程阻塞原因:
Java.io包中的同步Socket I/O服务器应用程序中最常见的阻塞I/O形式就是套接字读取和写入,虽然InputStream和OutputStream的read和write等方法不会造成中断,但关闭底层的套接字可以使得read和write等方法抛出一个SocketExceptionJava.io包中的同步I/O- 中断
InterruptibleChannel上的等待线程,抛出ClosedByInterruptException并关闭链路 - 关闭一个
InterruptibleChannel将导致所有在链路操作上阻塞的线程都抛出AsynchronousCloseException
- 中断
Selector的异步I/O- 获取某个锁
通过改写interrupt方法将非标准的取消操作封装在Thread中
public class ReaderThread extends Thread {
private final Socket socket;
private final InputStream in;
public ReaderThread(Socket socket) throws IOException {
this.socket = socket;
this.in = socket.getInputStream();
}
public void interrupt() {
try {
socket.close();
} catch (IOException ignored) {
} finally {
super.interrupt();
}
}
public void run() {
try {
byte[] buf = new byte[BUFSZ];
while(true) {
int count = in.read(buf);
if(count < 0)
break;
else if(count > 0)
processBuffer(buf, count);
}
} catch(IOException e){}
}
}
通过newTaskFor将非标准的取消操作封装在一个任务中
public abstract class SocketUsingTask<T> implements CancellableTask<T> {
@GuardedBy("this")
private Socket socket;
protected synchronized void setSocket(Socket s) {
socket = s;
}
public synchronized void cancel() {
try {
if(socket != null)
socket.close();
} catch (IOException ignored) {
}
}
public RunnableFuture<T> newTask() {
return new FutureTask<T>(this) {
public boolean cancel(boolean mayInterruptIfRunning) {
try {
SocketUsingTask.this.cancel();
} finally {
return super.cancel(mayInterruptIfRunning);
}
}
};
}
}