场景: 我们要计算1+…+10和20+…+30相加的结果,当然可以用a=1+…+10,b=20+…+30,之后resutl=a+b。 但实际上第一个任务A和第二个任务B互不影响, 我们可以使用多线程的方法,将任务A和任务B并行执行,最后将两个任务的执行结果相加。那这样怎么用java实现呢?
先提出两个问题:
- FutureTask和callable是用来干什么的?
- FutureTask实现并行计算(FutureTask的使用)
Future、FutureTask、Callable解释
Future、Callable是个接口,FutureTask是Future接口的实现类,通常是Callable和FutureTask一起使用达成异步任务返回值的目的。
Future接口Future接口有5个方法:
- cancel(boolean): 取消任务。传入的参数为表示若任务开始执行了,是否要尝试中断该线程。参数true为进行尝试中断,false为不进行中断。
- isCanceled(): 获取该任务是否被中断了
- isDone(): 该任务是否已经完成
- get(): 获取该任务的返回值,若任务还未完成则会阻塞。
- get(Long, TimeUnit): 获取任务的返回值,若任务还未完成则会阻塞。参数为指定的阻塞的最长时间,超时会抛出TimeoutException。
Callable只有一个call方法,实现的Callable接口的call方法则为我们要执行的异步任务。那为什么要新建一个接口而不是直接使用Runnable的run方法呢?
实际上,run方法的返回声明为void,因此我们编写的异步任务要有返回值的话,必须要另起一个call方法了。
FutureTask类实现了Future和Runnable接口,是我们实现线程异步执行,并能获取执行结果的封装的实现类。FutureTask有两个构造方法:
- FutureTask(Callable< V> callalbe): 通常的构造方法,Callable为异步任务,通过futureTask的get()方法可以阻塞地获取任务的返回值。
- FutureTask(Runnable runnable, V result): runnable为异步任务,result保存了执行结果,当futureTask的get()方法返回的就是result对象。当然,我们若是关心任务是否执行完成而不关心任务的返回值时,可以使用该构造方法。
因此解答第一个问题: FutureTask和Callable配合使用,用来获取异步线程的执行结果(而之前Runnable不能)。
并行计算1+…+10和20+…+30的结果接下来回到开头提出的问题,怎么用FutureTask和Callable实现并行计算两个任务呢?
1、手动new Thread()使用Callable是个带返回结果的任务,FutureTask封装获取Callable任务的类,但具体新建线程还是离不开Thread。因此,对于开头提到的问题,我们可以这样实现:
public static void main(String[] args) throws ExecutionException, InterruptedException { FutureTask2、配合线程池使用futureTask1 = new FutureTask<>(() -> { int res = 0; // 这里用lambda匿名表达式新建了一个Callable的实现 for (int i = 0; i <= 10; i++) { res += i; } return res; }); FutureTask futureTask2 = new FutureTask<>(() -> { int res = 0; for (int i = 20; i <= 30; i++) { res += i; } return res; }); Thread thread1 = new Thread(futureTask1); // FutureTask实现了Runnable接口,所以可以作为参数构造Thread() Thread thread2 = new Thread(futureTask2); // 实际上FutureTask的run方法就是在运行Callable的call方法 thread1.start(); thread2.start(); System.out.println(futureTask1.get() + futureTask2.get()); // 先获取任务1的值,未完成则会阻塞;获取任务2的值同理 }
当然,线程池为我们封装好了线程,使用线程池实现异步计算也更加优雅:
public static void main(String[] args) throws ExecutionException, InterruptedException { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 3, 5, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); Future执行结果future1 = threadPoolExecutor.submit(()->{ // 这里线程池submit执行的是call方法,返回的是FutureTask对象 int res = 0; // 这里用lambda匿名表达式新建了一个Callable的实现 for (int i = 0; i <= 10; i++) { res += i; } return res; }); Future future2 = threadPoolExecutor.submit(()->{ int res = 0; for (int i = 20; i <= 30; i++) { res += i; } return res; }); System.out.println(future1.get() + future2.get()); }
上述只是一个简单的示例,两位数之和性能体现不出什么。在实际测试时,将任务变为10亿级的,即0-1000000000之和加上2000000000-3000000000亿之和,若是传统的同步的方法,耗时约500ms。而使用上述的并行的方法,耗时三百多ms。符合预期。
public static void main(String[] args){ long startTime =System.currentTimeMillis(); long res = 0; for (long i = 0; i <=1000000000 ; i++) { res += i; } for (long i = 2000000000; i <=3000000000L ; i++) { res += i; } System.out.println(res); long endTime = System.currentTimeMillis(); System.out.println("执行时间为:" + (endTime - startTime)); }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)