JAVA多线程-Future和Callable

本文深入解析Runable的局限,并对比Future和Callable的特性,阐述Future如何处理异步任务的返回值和异常。重点讲解Future的主要方法如get、isDone、超时处理及FutureTask的应用实例,以及如何利用线程池简化并发操作。
摘要由CSDN通过智能技术生成

1、Runable的缺点

无法获取返回值

Runable中的run方法,无法抛出异常

2、Future和Callable的关系

2.1、Callable

有返回值,且可以抛出异常

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;
}

2.2、Future作用

如果遇到一个耗时的方法,让子线程去执行,我不用等待,可以去做其他的事情,直到我想要获取结果的时候,再去获取

2.3、Callable和Future的关系

可以用Future的get方法获取接口的返回值,通过isDone来判断任务是否执行完毕,以及取消这个任务,或者限时获取任务的结果

在call方法未执行完之前,调用get方法,我们的主线程会进入阻塞,等待任务完成后,call方法才会返回结果,主线程才会切换回Runable状态

Future可以理解为一个存储器,存储了call方法返回的结果,任务执行的时间是无法预知的,这取决于call方法的执行状态

3、Future的主要方法

3.1、get

get最简单的使用方法

/**
 * @Classname OneFuture
 * @Description 演示一个简单的Future使用方法
 * @Date 2021/5/23 16:05
 * @Created by WangXiong
 */
public class OneFuture {

    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(5);
        Future<Integer> future = service.submit(new CallableTask());
        try {
            Integer integer = future.get();
            System.out.println(integer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        service.shutdown();
    }

    static class CallableTask implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            Thread.sleep(3000);

            return new Random().nextInt();
        }
    }
}

使用Lambda表达式

/**
 * @Classname OneFuture
 * @Description 使用lambda表达式来实现
 * @Date 2021/5/23 16:05
 * @Created by WangXiong
 */
public class OneFutureLambda {

    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(5);
        Callable<Integer> callable = ()->{
            Thread.sleep(3000);
            return new Random().nextInt();
        };
        Future<Integer> future = service.submit(callable);
        try {
            Integer integer = future.get();
            System.out.println(integer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        service.shutdown();
    }

}

 多个任务,用Future数组来获取结果

/**
 * @Classname MultiFutures
 * @Description 演示批量提交任务,用list来接受结果
 * @Date 2021/5/23 16:19
 * @Created by WangXiong
 */
public class MultiFutures {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);
        ArrayList<Future<Integer>> list = new ArrayList<>();
        Callable<Integer> callable = ()->{
            Thread.sleep(3000);
            return new Random().nextInt();
        };
        for (int i = 0; i < 20; i++) {
            Future<Integer> future = service.submit(callable);
            list.add(future);
        }
        for (int i = 0; i < list.size(); i++) {
            Future<Integer> future = list.get(i);
            try {
                Integer integer = future.get();
                System.out.println(integer);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        service.shutdown();
    }
}

3.2、isDone

isDone会判断任务是否执行完毕

任务中抛出异常,isDone方法演示

  • 只有调用get方法后,才可以感知到任务中抛出的异常
  • 抛出的异常都是ExecutionException异常
  • isDone,如果任务运行结束,会返回true
/**
 * @Classname GetExecption
 * @Description 演示Future中抛出异常,for循环是为了演示抛出的时机:
 * 不是一开始就抛出异常,直到get的时候才抛出异常
 * @Date 2021/5/23 16:27
 * @Created by WangXiong
 */
public class GetExecption {

    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(5);
        CallableTask task = new CallableTask();
        Future<Integer> future = service.submit(task);
        for (int i = 0; i < 5; i++) {
            System.out.println(i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            System.out.println(future.isDone());
            future.get();
        } catch (InterruptedException e) {
            System.out.println("抛出异常InterruptedException");
            e.printStackTrace();
        } catch (ExecutionException e) {
            System.out.println("抛出异常ExecutionException");
            e.printStackTrace();
        }
        service.shutdown();
    }

    static class CallableTask implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            throw new RuntimeException("抛出异常");
        }
    }
}

3.3、get(long timeout, TimeUnit unit)

演示get的超时方法

/**
 * @Classname TimeOut
 * @Description 演示get的超时时间,超时后需要处理,调用cancel,传入true和false的区别,
 * 代表是否中断正在执行的任务
 * @Date 2021/5/23 18:54
 * @Created by WangXiong
 */
public class TimeOut {

    private static final Ad DEFAULT_AD = new Ad("无网络时,默认广告");
    private static final ExecutorService exec = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        TimeOut timeOut = new TimeOut();
        timeOut.printAd();
    }

    public void printAd(){
        Future<Ad> future = exec.submit(new FetchAdTask());
        Ad ad = null;
        try {
            ad = future.get(2000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
            ad = new Ad("被中断时候的默认广告");
        } catch (ExecutionException e) {
            e.printStackTrace();
            ad = new Ad("异常时候的默认广告");
        } catch (TimeoutException e) {
            e.printStackTrace();
            ad = new Ad("超时时候的默认广告");
            //cancel()中传入true,代表直接中断
            //cancel()中传入false,代表让线程执行完后中断
            boolean cancel = future.cancel(true);
            System.out.println("cancel的值为:"+cancel);
        }
        exec.shutdown();
        System.out.println(ad);
    }

    static class FetchAdTask implements Callable<Ad> {

        @Override
        public Ad call() throws Exception {
            try {
                Thread.sleep(3000);
            }catch (InterruptedException e){
                System.out.println("sleep中被中断");
                return new Ad("被中断时候的默认广告");
            }
            return new Ad("订票哪家强?上东找南翔");
        }
    }

    static class Ad{
        private String name;
        public Ad(String name){
            this.name = name;
        }

        @Override
        public String toString() {
            return "Ad{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
}

测试

3.4、cancel方法

cancel方法的代码演示可以看上面的代码演示

cancel方法执行的三种情况:

  • cancel执行的任务还没有开始,任务会被正常取消,未来也不会执行,返回true
  • 如果任务已经完成或取消,那么cancel执行会失败,返回false
  • 如果任务执行到一半,会根据传入的参数进行处理,参数是true,会发出中断信号;如果传递的是false,则不会发送中断信号

4、用FutureTask来创建Future

FutureTask的实现关系,他同时实现了Runable和Future两个接口

代码演示

/**
 * @Classname FutureTaskDemo
 * @Description 演示FutureTask的demo
 * @Date 2021/5/23 23:13
 * @Created by WangXiong
 */
public class FutureTaskDemo {

    public static void main(String[] args) {
        Task task = new Task();
        FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(task);
        new Thread(integerFutureTask).start();
        try {
            System.out.println("运行结果:"+integerFutureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    static class Task implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            System.out.println("子线程正在计算");
            Thread.sleep(3000);
            Integer sum = 0;
            for (int i = 0; i < 100; i++) {
                sum += i;
            }
            return sum;
        }
    }
}

测试

使用线程池

/**
 * @Classname FutureTaskDemo
 * @Description 演示FutureTask的demo
 * @Date 2021/5/23 23:13
 * @Created by WangXiong
 */
public class FutureTaskDemo {

    public static void main(String[] args) {
        Task task = new Task();
        FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(task);
        // new Thread(integerFutureTask).start();
        //使用线程池
        ExecutorService service = Executors.newCachedThreadPool();
        service.submit(integerFutureTask);
        try {
            System.out.println("运行结果:"+integerFutureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    static class Task implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            System.out.println("子线程正在计算");
            Thread.sleep(3000);
            Integer sum = 0;
            for (int i = 0; i < 100; i++) {
                sum += i;
            }
            return sum;
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值