重点是那个计算年销售额的例子,认真看三遍。本文花了三个小时。java
GitHub欢迎star。git
小白认为学习语言最好的方式就是模仿、思考别人为何这么写。程序员
建立线程要么是实现Runnable接口,要么实现Thread类继承。虽然这么作很简单,但建立出的线程会受到严重的限制--run方法不返回任何值给建立者。所以,许多程序员采用将返回值写到文件中这样的不完美解决方案。Runnable的另外一个问题在于不能抛出任何异常,所以必须在run方法中处理全部的异常。幸运的是,J2SE5提供了Callable和Future接口,用于解决此类的需求,Thread模拟了一类 没有返回结果的任务的执行, 而Callable则是模拟 具备返回结果的任务的执行。 Future更进一步,模拟一类 可检查进度并能够取回结果的任务的执行github
CCallable接口相似于Runnable接口,拥有接受单个参数的call方法。数组
public interface Callable<V> { V call() throws Exception }
call方法能够返回类型参数中指定的任意类型。须要注意的是,call方法和Runnable接口中的run方法并不相同,这里的call方法会抛出被检查的异常。使用Callable<void>可实现没有返回值的Callable。框架
不能直接将Callable提交到Thread中执行,而是必须使用ExecutorService
来执行Callable对象。,能够经过调用submit方法来完成这项任务。dom
<T> Future <T> submit (Callable <T> task)
submit方法返回一个Future对象异步
当调用方法将任务提交到Executor时,Executor会返回Future对象给调用方,Future接口的定义以下;函数
interface Future <V>
其中V表明的是Future中get方法返回值类型。调用方经过这个Future对象获取所请求任务的控制权。get方法会将结果返回给调用方。若是计算没有结束,get方法则会一直等下去,重载的get方法能够接受时限参数,从而限制任务的最长执行时间。isDone方法检测热恩物是否执行结束,若是结束,就返回true。cancel方法会在任务正常结束以前尝试取消,若是任务正常结束以前被取消,isCancelled会返回true学习
FutureTask是封装类实现类Future与Runnable接口,并提供了一种便捷的方式,能够将Callable转为Future与Runnable。
Caller是实现类Callable接口的Java类。Callable接口仅含有call方法,程序员在实现Callable接口时必须实现call方法。call方法一般会包含服务实现。要调用服务就必须实例化Caller类并调用call方法。若是但愿上述过程异步完成,就须要采用某些特定机制调用call方法。Callbale接口和Runnable接口不一样,后者能够放在Thread类的构造函数中执行,Java提供了另外一个名为ExecutorService
的类用于执行可调用任务。调用方首先建立或获取ExecutorServie实例,并提交可调用任务以待执行。接下来框架会返回Future对象给调用方,这个Future队形能够用于检查可调用任务的状态并获取执行结果。
重点知识在main函数中
/** * Created by guo on 2018/2/15. * 计算年销售额 */ public class AnnualSalesCalc { private static int NUMBER_OF_CUSTOMERS = 100; //表示矩阵的行数 private static int NUMBER_OF_MONTHS = 12; //表示矩阵的列数 private static int salesMatrix[][]; //表明还没有建立的二维整形数组。 /** * Summer用来计算每行的和。 * Summer类被声明在AnnualSalesCalc的内部类,而且为private和static。 * 之因此声明为private,是由于Summer类不会用在其余地方。 * 声明为静态是应为Summer类会被静态的main方法调用。Summer类还实现类Callable接口。 * 构造函数接受ID的整数值,并存储在类变量中以备后用。 */ private static class Summer implements Callable { private int companyID; public Summer(int companyID) { this.companyID = companyID; } /** * Call方法被映射为任何类型的泛型类,在这里被映射为Integer类型 * call方法经过for循环遍从来计算矩阵特定行内的全部元素的总和 */ public Integer call() throws Exception { int sum = 0; // for (int col = 0; col < NUMBER_OF_MONTHS; col++) { sum += salesMatrix[companyID][col]; } //在求和计算完成以后,程序会打印一条消息给用户, // 代表这个Callable对象的任务已经结束,而且不管何时被询问,均可以将计算结果返回给调用者。 System.out.printf("Totaling for client 1%02d completed%n", companyID); return sum; } } public static void main(String[] args) throws Exception { generateMatrix(); printMaxtrix(); //咱们须要执行者服务调用Callable对象,Executor类提供此服务 //newFixedThreadPool是Executors类的静态方法,接受一个整型参数, //该参数的值决定了这个方法建立的线程数目。 //该方法会建立固定的线程池,用于执行不一样的任务,而且在结束时会返回一个ExecutorService实例。 ExecutorService executor = Executors.newFixedThreadPool(10); //声明的Set对象用于存储Future对象,从而监控提交的任务。 Set<Future<Integer>> set = new HashSet<Future<Integer>>(); for (int row = 0; row < NUMBER_OF_CUSTOMERS; row++) { //为矩阵的每一行都实例化类Summer对象,Summer对象同时也是Callable对象 Callable<Integer> callable = new Summer(row); //经过调用前面建立的executor对象的submit方法来运行这个Callable对象; //submit方法将Callable对象提交给线程池中的某一个线程,并返回一个Future对象给调用者。 //调用方能够使用这个Future对象的get方法来获取计算结果。 Future<Integer> future = executor.submit(callable); //咱们将返回的Future对象放在集合中,以便在最后阶段获取集合中全部元素之和,进而计算总和 set.add(future); } int sum = 0; //在提交 全部任务以后,使用for-each循环计算总和 for (Future<Integer> future : set) { sum += future.get(); } System.out.printf("%n The annual turnover (bags) : %s%n%n", sum); //最后关闭执行者服务,以释放分配的全部资源。 executor.shutdown(); } }
为了清晰,我把这两个方法从类中拿出来了。
/** * 生成矩阵 */ private static void generateMatrix() { salesMatrix = new int[NUMBER_OF_CUSTOMERS][NUMBER_OF_MONTHS]; for (int i = 0; i < NUMBER_OF_CUSTOMERS; i++) { for (int j = 0; j < NUMBER_OF_MONTHS; j++) { //数组中的每一个元素都被初始化为0-99之间的随机数。 salesMatrix[i][j] = (int) (Math.random() * 100); } } } /** * 打印矩阵 */ private static void printMaxtrix() { System.out.print("\t\t"); String[] monthDisplayName = new DateFormatSymbols().getShortMonths(); for (String strName : monthDisplayName) { System.out.printf("%8s", strName); } System.out.printf("\n\n"); for (int i = 0; i < NUMBER_OF_CUSTOMERS; i++) { System.out.printf("Client ID : 1%02d", i); for (int j = 0; j < NUMBER_OF_MONTHS; j++) { System.out.printf("%8d", salesMatrix[i][j]); } System.out.println(); } System.out.println("\n\n"); }
输出以下:
一月 二月 三月 四月 五月 六月 七月 八月 九月 十月 十一月 十二月 Client ID : 100 89 63 98 51 1 58 69 67 81 97 98 64 Client ID : 101 66 83 3 83 75 43 10 83 92 39 27 75 Client ID : 102 10 88 74 38 33 91 43 82 93 94 28 25 ...省略.. Client ID : 197 50 39 43 33 18 61 67 21 11 56 11 19 Client ID : 198 64 5 10 63 97 52 99 35 15 9 73 29 Client ID : 199 47 95 62 51 7 78 68 60 53 58 23 56 Totaling for client 101 completed Totaling for client 102 completed Totaling for client 110 completed ...省略.. Totaling for client 105 completed Totaling for client 109 completed The annual turnover (bags) : 60152