预见猿份
主题
首页面试题模拟面试SQL练习在线工具关于我们老苗一对一私教学员评价
实战项目
零基础学习路线项目前置基础创新WMS项目Java微服务框架与实战云岚到家项目闪聚支付项目学成在线项目青橙电商项目JVM原理与实战调优分布式事务专题Java高频面试题MySQL从入门到精通达梦数据库从入门到实战MongoDB从入门到实战PostgreSQL从入门到实战TiDB从入门到实战Java数据结构与算法Java 并发编程(JUC)实战课程老苗一对一私教学员评价
blog我的会员
  • Java 并发编程(JUC)实战课程

    • 课程介绍
    • 01 多线程入门
    • 02 线程的常用方法
    • 03 线程池
    • 04 synchronized教程
    • 05 ReentrantLock教程
    • 06 读写锁
    • 07 JMM教程
    • 08 CAS教程
    • 09 wait&notify
    • 10 常用的并发集合
    • 11 异步任务控制
    • 12 常用并发工具







----- 到底线了 -----

第11章 异步任务控制 ​

3.1 Future ​

3.1.1 定义任务的三种方式 ​

最初学习多线程时就学习了什么是任务,什么是线程,线程用来执行任务,任务包含业务逻辑,创建一个任务并执行任务有哪些方式呢?下边进行汇总。

1、继承Thread类。

Thread类本身实现了Runnable接口,继承Thread类重写run方法可以创建一个任务,调用thread对象的start即可启动一个线程并执行任务,由于Java是单继承模式,继承Thread类将无法再继承其它类,所以不推荐使用此方式。

2、实现java.lang.Runnable接口

实现java.lang.Runnable接口定义独立的任务对象,有如下方法:

1)使用Thread类的Thread(Runnable target) 构造方法

2)使用ExecutorService的void execute(Runnable command)

3、实现java.util.concurrent.Callable接口

与Runnable接口不同的是Callable接口有返回值,Callable接口如下:

java
public interface Callable<V> {
    V call() throws Exception;
}
1
2
3
4

通过ExecutorService的Future submit(Callable task)方法提交Callable任务得到一个Future返回值,Future表示未来可能返回的结果,通过Future接口的方法可以获取任务的返回值,判断任务是否完成等操作,如下:

配图21

get()和get(long,TimeUnit)两个方法可以获取任务执行结果,两个方法都是阻塞方法,其中get()直到任务完成为止,get(long,TimeUnit)方法可以设置超时时间,如果达到超时时间任务还没有结束则抛出异常。

isDone():非阻塞方法,获取任务是否完成。

isCancelled(): 非阻塞方法,如果任务完成前被终止则返回true。

cancel(boolean)取消任务。

查询API文档如下:

配图22

3.1.2 定义具有返回值的任务 ​

下边测试如何定义一个具有返回值的任务。

定义一个带有返回值的任务需要实现Callable接口,如下:

java
@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}
1
2
3
4
5
6
7
8
9
10

下边实现求1到100这几个数的和,测试代码如下:

java
package com.yjoffer.javase.thread.utils;

import com.yjoffer.javase.config.Logger;

import java.util.concurrent.*;

/**
 * 测试任务控制
 * @author 预见猿份(www.yjoffer.com)
 * @version 1.0
 **/
public class FutureTest {

    private static Logger logger = Logger.getLogger(FutureTest.class);

    //测试Future
    public static void test_future(){

        ExecutorService threadPool = Executors.newCachedThreadPool();

        //定义一个future
        Future<Long> future = threadPool.submit(() -> {
            logger.debug("开始任务");
            long sum = 0;
            for (int i = 1; i <= 100; i++) {
                sum += i;
            }
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //返回结果
            logger.debug("返回结果");
            return sum;
        });

        //获取结果
        try {
            logger.debug("获取结果");
            Long result = future.get();
            logger.debug("result="+result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        test_future();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

输出:

java
15:42:23.120 FINE com.yjoffer.javase.thread2.basic.FutureTest[main] - 获取结果
15:42:23.120 FINE com.yjoffer.javase.thread2.basic.FutureTest[pool-1-thread-1] - 开始任务
15:42:28.142 FINE com.yjoffer.javase.thread2.basic.FutureTest[pool-1-thread-1] - 返回结果
15:42:36.38 FINE com.yjoffer.javase.thread2.basic.FutureTest[main] - result=5050
1
2
3
4

在future.get();处打断点,get()只是获取结果不影响 任务的执行。

get(long,TimeUnit)等待指定时间后,如果任务没有执行完成则抛出异常java.util.concurrent.TimeoutException。

下边的代码执行任务至少需要5秒,get()方法等待3秒,所以最终由于get()方法的超时提前到达导致抛出TimeoutException异常,测试代码如下:

java
//测试Future,测试get方法超时
    public static void test_future2(){

        ExecutorService threadPool = Executors.newCachedThreadPool();

        //定义一个future
        Future<Long> future = threadPool.submit(() -> {
            logger.debug("开始任务");
            long sum = 0;
            for (int i = 1; i <= 100; i++) {
                sum += i;
            }
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //返回结果
            logger.debug("返回结果");
            return sum;
        });

        //获取结果
        try {
            logger.debug("获取结果");
            Long result = future.get(3,TimeUnit.SECONDS);
            logger.debug("result="+result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }

    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

运行程序,输出:

java
15:45:43.38 FINE com.yjoffer.javase.thread2.basic.FutureTest[main] - 获取结果
15:45:43.38 FINE com.yjoffer.javase.thread2.basic.FutureTest[pool-1-thread-1] - 开始任务
java.util.concurrent.TimeoutException
	at java.util.concurrent.FutureTask.get(FutureTask.java:205)
	at com.yjoffer.javase.thread2.basic.FutureTest.test_future2(FutureTest.java:75)
	at com.yjoffer.javase.thread2.basic.FutureTest.main(FutureTest.java:88)
15:45:48.64 FINE com.yjoffer.javase.thread2.basic.FutureTest[pool-1-thread-1] - 返回结果
1
2
3
4
5
6
7

3.1.3 异常处理 ​

如果任务在执行时出现异常,外界可以拿到异常信息吗?

测试代码如下:

java
//测试Future,测试异常处理
    public static void test_future3(){

        ExecutorService threadPool = Executors.newCachedThreadPool();

        //定义一个future
        Future<Long> future = threadPool.submit(() -> {
            logger.debug("开始任务");
            long sum = 0;
            for (int i = 1; i <= 100; i++) {
                sum += i;
            }
            int i=1/0;//制造异常
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //返回结果
            logger.debug("返回结果");
            return sum;
        });

        //获取结果
        try {
            logger.debug("获取结果");
            Long result = future.get();
            logger.debug("result="+result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (Exception e) {
            logger.debug("异常处理...");
            e.printStackTrace();
        }

    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

运行程序,输出如下:

java
15:48:17.900 FINE com.yjoffer.javase.thread2.basic.FutureTest[main] - 获取结果
15:48:17.900 FINE com.yjoffer.javase.thread2.basic.FutureTest[pool-1-thread-1] - 开始任务
15:48:17.929 FINE com.yjoffer.javase.thread2.basic.FutureTest[main] - 异常处理...
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at com.yjoffer.javase.thread2.basic.FutureTest.test_future3(FutureTest.java:112)
	at com.yjoffer.javase.thread2.basic.FutureTest.main(FutureTest.java:124)
Caused by: java.lang.ArithmeticException: / by zero
	at com.yjoffer.javase.thread2.basic.FutureTest.lambda$test_future3$2(FutureTest.java:98)
	at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
	at java.util.concurrent.FutureTask.run(FutureTask.java)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

从输出可以看出:当任务执行过程出现异常,通过捕获get()方法异常即可获取。

🔒关注公众号,免费阅读全文

请免费关注 预见猿份 公众号,发送:yjoffer.com,获取解锁密码

公众号二维码微信扫码关注

关注公众号回复获取密码后,在下方输入并点击解锁,本机后续再读无需重复输入。

← 10 常用的并发集合12 常用并发工具 →








如果发现文档内容有错误或排版错乱,请及时联系站长老苗修改,不胜感激。联系我们
关于我们 | 隐私政策 | 豫ICP备2026003386号-4 | 豫公网安备41010202004008号
目录

本页无章节