本章主要介绍Excutor框架。
无限制创建线程的缺陷:
1.线程生命周期的开销非常高。
2.资源消耗。活跃的线程会消耗资源,尤其是内存。
3.稳定性。
Excutor框架
串行执行任务的问题在于糟糕的响应性和吞吐量,而为每一个任务分配一个线程的问题在于资源管理的复杂性。
线程池简化了线程的管理工作,并且java.util.concurrent提供了一种灵活的线程池实现作为Executor框架的一部分。
public interface Executor { public abstract void execute(Runnable runnable); }
虽然Executor是一个简单的接口,但它却为灵活且强大的异步任务执行框架提供了基础,该框架能支持多种不同类型的任务执行策略。它提供了一种标准的方法将任务的提交过程和执行过程解耦开来,并用Runnable表示任务。
创建一个固定长度的线程池:
public class TaskExecutionWebServer { private static final int NTHREADS = 100; private static final Executor exec = Executors.newFixedThreadPool(NTHREADS); @SuppressWarnings({ "resource", "unused" }) public static void main(String[] args) throws IOException { ServerSocket socket = new ServerSocket(80); while(true){ final Socket connection = socket.accept(); Runnable task = new Runnable() { @Override public void run() { //处理任务 } }; exec.execute(task); } } }
线程池
线程池,是指管理一组同构工作线程的资源池。
在线程池中执行任务,比为每一个任务分配一个线程,优势更多。
- 通过重用现有的线程而不是创建新线程,可以在处理多个请求时分摊在线程创建和销毁过程中产生的巨大开销。
- 当请求到达时,工作线程通常已经存在,因此不会由于等待创建线程而延迟任务的执行,从而提高的响应性。
- 通过适当调整线程池的大小,可以创建足够多的线程使处理器保持忙碌状态,同时还可以防止过多线程相互竞争资源而使应用程序耗尽内存或者失败。
创建线程池,使用Executors类的静态工厂方法:
1.newFixedThreadPool创建一个固定长度的线程池,每当提交一个任务时,就创建一个线程,直到达到线程池的最大数量,这时线程池的规模不再变化。
public static ExecutorService newFixedThreadPool(int i) { return new ThreadPoolExecutor(i, i, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); }2.newCachedThreadPool创建一个可缓存的线程池,如果线程池的当前规模超过了处理需求时,那么将回收空闲的线程,当需求增加时,可以添加新的线程,线程池的规模不存在任何限制。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue()); }
3.newSingleThreadExecutor是一个单线程的Executor,创建单个工作者线程执行任务。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); }
4.newScheduledThreadPool创建了一个固定长度的线程池,而且以延迟或者定时的方式执行任务。
public static ScheduledExecutorService newScheduledThreadPool(int i, ThreadFactory threadfactory) { return new ScheduledThreadPoolExecutor(i, threadfactory); }
Executor生命周期管理:
ExecutorService接口扩展了Executor,添加了一些用于生命周期管理的方法
public interface ExecutorService extends Executor { public abstract void shutdown(); public abstract List shutdownNow(); public abstract boolean isShutdown(); public abstract boolean isTerminated(); public abstract boolean awaitTermination(long l, TimeUnit timeunit) throws InterruptedException; public abstract Future submit(Callable callable); public abstract Future submit(Runnable runnable, Object obj); public abstract Future submit(Runnable runnable); public abstract List invokeAll(Collection collection) throws InterruptedException; public abstract List invokeAll(Collection collection, long l, TimeUnit timeunit) throws InterruptedException; public abstract Object invokeAny(Collection collection) throws InterruptedException, ExecutionException; public abstract Object invokeAny(Collection collection, long l, TimeUnit timeunit) throws InterruptedException, ExecutionException, TimeoutException; }
ExecutorService的生命周期有三种状态:运行、关闭和已终止。
ExecutorService在初始创建时处于运行状态。
shutdown方法将执行平缓的关闭过程:不再接受新的任务,同时等待已经提交的任务执行完毕,包括还未开始的任务。
shutdownNow方法将执行粗暴的关闭过程:将尝试取消所有运行中的任务,并且不再启动队列中尚未开始执行的任务。
ExecutorService关闭后提交的任务将由“拒绝执行处理器”来执行,它会抛弃任务,或者使得execute方法抛出一个未检查的RegectedExecutionException。等所有任务都完成后,ExecutorService进入终止状态。可以调用awaitTermination来等待ExecutorService到达终止状态,或者通过isTerminated轮询是否已经终止。通常在调用awaitTermination后会立即调用shutdown,从而产生同步关闭ExecutorService的效果。
import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; public class LifecycleWebServer { private final ExecutorService exec = Executors.newCachedThreadPool(); @SuppressWarnings("resource") 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() { //处理任务 hanleRequest(conn); } }); } catch (Exception e) { e.printStackTrace(); } } } public void stop(){ exec.shutdown(); } void hanleRequest(Socket conn){ //如果请求要求停止任务 stop(); //否则,处理任务 } }
携带结果的任务Callable与Future
许多任务都存在延迟的计算,对于这些任务,Callable是一个更好的抽象:它认为主入口点将返回一个值,并可能抛出一个异常。
Future标识一个任务的生命周期,并提供了相应的方法来判断是否已经完成或者取消,以及获取任务的结果和取消任务。
public interface Callable { public abstract Object call() throws Exception; }
public interface Future { public abstract boolean cancel(boolean flag); public abstract boolean isCancelled(); public abstract boolean isDone(); public abstract Object get() throws InterruptedException, ExecutionException; public abstract Object get(long l, TimeUnit timeunit) throws InterruptedException, ExecutionException, TimeoutException; }
public class FutureRenderer { private final ExecutorService executor = Executors.newCachedThreadPool(); void renderPage(CharSequence source){ final List<ImageInfo> imageInfos = scanForImageInfo(source); Callable<List<ImageData>> task = new Callable<List<ImageData>>() { @Override public List<ImageData> call() throws Exception { List<ImageData> result = new ArrayList<>(); for(ImageInfo imageInfo: imageInfos ){ result.add(imageInfo.downloadImage()); } return result; } }; Future<List<ImageData>> future = executor.submit(task); renderText(source); try { List<ImageData> imageData = future.get(); for(ImageData data : imageData){ renderImage(data); } } catch (Exception e) { future.cancel(true); } } private void renderImage(ImageData data) { //加载图片 } private void renderText(CharSequence source) { //加载文字 } private List<ImageInfo> scanForImageInfo(CharSequence source) { return null; } class ImageInfo { public ImageData downloadImage() { return null; } } class ImageData{ } }
如果向Executor提交一组计算任务,并且希望在计算完成后获得结果,那么可以保留与每个任务关联的Future,然后反复使用get方法,同时将参数timeout指定为0,从而通过轮询来判断任务是否完成。这种方法虽然可行,但是有些繁琐。
可以使用完成服务CompletionService将Executor和BlockingQueue的功能融合在一起。可以将Callable任务提交给它来执行,然后使用类似于队列操作的take和poll等方法来获得已经完成的结果,而这些结果会在完成时被封装到Future。
public class Renderer { private final ExecutorService executorService; public Renderer(ExecutorService executorService) { this.executorService = executorService; } void renderPage(CharSequence source){ final List<ImageInfo> imageInfos = scanForImageInfo(source); CompletionService<ImageData> completionService = new ExecutorCompletionService<>(executorService); for(ImageInfo imageInfo: imageInfos ){ completionService.submit(new Callable<Renderer.ImageData>() { @Override public ImageData call() throws Exception { return imageInfo.downloadImage(); } }); } renderText(source); try { for(int t=0 ,n =imageInfos.size();t<n ; t++){ Future<ImageData> f =completionService.take(); ImageData imageData = f.get(); renderImage(imageData); } } catch (Exception e) { e.printStackTrace(); } } private void renderImage(ImageData data) { //加载图片 } private void renderText(CharSequence source) { //加载文字 } private List<ImageInfo> scanForImageInfo(CharSequence source) { return null; } class ImageInfo { public ImageData downloadImage() { return null; } } class ImageData{ } }