一、创建任务
任务可以理解为一段独立执行的代码,它完成一定的功能。
在Java 5及以上版本中不仅可以利用 Runnable接口,还可以利用Callable接口。
两种任务的区别:Runnable 的run()方法完成的任务是没有返回值的 - void
而Callable 的call()方法完成的任务是有返回值的 - 返回值 有接口的泛型<V> 来制定
二、执行任务
通过java.util.concurrent.ExecutorService接口对象来执行任务,
这个接口对象由工具类java.util.concurrent.Executors的静态方法来创建。
相关类及接口说明:
Executors 提供建立线程池或线程工厂的方法.
ExecutorService 提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。
Executor 可以简单理解为一个线程的执行者,是Executors 的上一级接口。
Callable 线程要执行而去实现的接口
Future 表示异步计算的结果,对执行的线程的状态检查,可以获得执行结果.
下面以一个例子来说明:
public class CallableDemo { public static void main(String[] args) { /** * Executors.newFixedThreadPool(5)创建一个线程池, * 这个线程池中维护 5 个线程的执行与销毁工作,并将维护的工作交给 ExecutorService 去处理 */ ExecutorService service = Executors.newFixedThreadPool(5); // ExecutorService service = Executors.newCachedThreadPool(); /** * 记录线程的 任务(这里由实现了Callable接口 的类来执行)的返回接口 */ List<Future<String>> resultList = new ArrayList<Future<String>>(); /** * 创建 10个任务 */ for (int i = 0; i < 10; i++) { /** * ExecutorService 的 submit方法 来维护任务(这个任务不是线程,是Callable,但是和线程的工作原理是一样的)的运行状态 */ Future<String> future = service.submit(new TaskWithResult(i)); //将任务的结果加入 resultList.add(future); } //遍历结果 for (Future<String> f : resultList) { try { System.out.println("Future result: " + f.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } finally { service.shutdown(); } } } }
class TaskWithResult implements Callable<String> { private int id; public TaskWithResult(int id) { this.id = id; } /** * 这里的返回类型要与Callable制定的泛型类型要一致 */ @Override public String call() throws Exception { int delay = (int) (Math.random() * 3000); /** * 让线程休眠一下,以便于结果查看 */ System.out.println("Call executed ...." + Thread.currentThread().getName() + " - sleep: " + delay + " ms"); Thread.sleep(delay); return "call feedback result " + this.id; } }
查看输出:
Call executed ....pool-1-thread-2 - sleep: 1560 ms Call executed ....pool-1-thread-3 - sleep: 467 ms Call executed ....pool-1-thread-1 - sleep: 1619 ms Call executed ....pool-1-thread-5 - sleep: 2817 ms Call executed ....pool-1-thread-4 - sleep: 72 ms Call executed ....pool-1-thread-4 - sleep: 711 ms Call executed ....pool-1-thread-3 - sleep: 392 ms Call executed ....pool-1-thread-4 - sleep: 2248 ms Call executed ....pool-1-thread-3 - sleep: 2537 ms Call executed ....pool-1-thread-2 - sleep: 646 ms Future result: call feedback result 0 Future result: call feedback result 1 Future result: call feedback result 2 Future result: call feedback result 3 Future result: call feedback result 4 Future result: call feedback result 5 Future result: call feedback result 6 Future result: call feedback result 7 Future result: call feedback result 8 Future result: call feedback result 9
看线程的名字都是类似于“pool-1-thread-4” 的字符串,可是为什么最大的索引是5呢?
对了,这里就与线程池中维护的线程数量有关了 当你改变线程池的大小时,这里的数据时会跟着变的,
service.submit(Callable callable)是来运行 Callable类型的任务,也可运行Runnable类型的任务
而
service.execute(Runnable runnable)只能用来运行 Runnable类型的任务
注:这个execute方法是其父借口java.util.concurrent.Execute中的一个方法
大家也可以试试
ExecutorService service = Executors.newCachedThreadPool();
看输出的名字Thread名字会有什么变化