从JDK1.5开始,增加了一个执行并行任务的框架——Executor框架。框架在java.util.concurrent包中。Executor是框架中的一个接口,使用Executor可以同步或异步地执行任务。
异步任务可以放在多线程中处理,但使用Executor比直接创建线程处理任务有很多好处,比如设置任务开始时间,取消任务队列,控制任务队列执行策略等,而且使用Executor可以很容易地创建线程池。
线程池
Executors是一个工厂类,可以方便的创建各种类型Executor或线程池:
- newSingleThreadExecutor():创建一个Executor,顺序执行一个任务队列,每次只能执行一个任务。
- newFixedThreadPool(nThreads):创建一个有固定数目线程的线程池,每次最多执行nThreads个任务,如果任务多于nThreads,多于的线程置于等待队列中
- newCachedThreadPool():创建一个线程池,线程数目会随任务数目增加而增加,同时也会回收已经空闲的线程。
- newScheduledThreadPool(corePoolSize):创建一个线程池,可以让任务延迟或周期性执行。
执行单个任务
- Executor executor = Executors.newSingleThreadExecutor();
- Runnable task = new Runnable() {
- @Override
- public void run() {
- System.out.println("Executing task...");
- }
- };
- executor.execute(task);
创建一个线程池
- final int pool_size = 5;
- Executor executor = Executors.newFixedThreadPool(pool_size);
- //or
- //Executor executor = Executors.newCachedThreadPool();
- int nTasks = 10;
- for (int i = 0; i < nTasks; ++i){
- Runnable task = createTask();
- executor.execute(task);
- }
生命周期
Executor有个缺点,一旦任务提交,就无法获得任务的执行情况,也无法停止,除非等待所有任务执行完毕或强制关闭JVM。
ExecutorService扩展了Executor接口,加入了生命周期管理。ExecutorService可以在运行时关闭,关闭后ExecutorService就停止接收新的任务。对已经接收到的任务有两种处理方法:如果调用shutdown()方法,ExecutorService会等待所有接收到的任务执行完毕;如果调用shutdownNow()方法,ExecutorService会取消所有等待的任务并尝试停止正在执行的任务。
ExecutorService.java中的例子,关闭线程池:
- void shutdownAndAwaitTermination(ExecutorService pool) {
- pool.shutdown(); // Disable new tasks from being submitted
- try {
- // Wait a while for existing tasks to terminate
- if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
- pool.shutdownNow(); // Cancel currently executing tasks
- // Wait a while for tasks to respond to being cancelled
- if (!pool.awaitTermination(60, TimeUnit.SECONDS))
- System.err.println("Pool did not terminate");
- }
- } catch (InterruptedException ie) {
- // (Re-)Cancel if current thread also interrupted
- pool.shutdownNow();
- // Preserve interrupt status
- Thread.currentThread().interrupt();
- }
- }
延迟、周期性执行任务
ScheduledExecutorService扩展了ExecutorService接口,可以延迟或间期执行任务。
- schedule(command, delay, TimeUnit): 任务延迟delay时间后执行
- scheduleAtFixedRate(command, initDelay, period, TimeUnit.SECONDS): 先延迟initDelay时间,然后每period时间执行一次任务
- scheduleWithFixedDelay(command, initDelay, delay, TimeUnit.SECONDS): 先延迟initDelay时间,然后执行任务,每次执行完毕后延迟delay时间再执行下次任务。
注意:scheduleAtFixedRate和scheduleWithFixedDelay的区别。scheduleAtFixedRate执行任务的周期与任务消耗的时间没有关系,如果任务消耗10秒,但设置的周期是1秒,那1秒后下次任务就会开始。而scheduleWithFixedDelay不同,一定要等本次任务完成后,间隔delay秒才执行下一个任务。
延迟、周期性执行任务:
- final int pool_size = 1;
- ScheduledExecutorService scheduleService = Executors.newScheduledThreadPool(pool_size);
- Runnable command = new Runnable() {
- @Override
- public void run() {
- System.out.println("Executing task...");
- }
- };
- scheduleService.schedule(command, 10, TimeUnit.SECONDS);
- //scheduleService.scheduleAtFixedRate(command, 10, 1, TimeUnit