走进Callable
Callable接口类似于Runnable接口,因为他们都是为其实例可能由另一个线程执行的类执行设计的,然后Runnable接口既不抛出异常,也不返回结果(没有返回值)
Runnable没有返回值,只能开辟不能接收
Callable总结有以下三种特点
- 可以有返回值
- 可以抛出异常
- 方法不同,Runnable使用run(),Callable使用call()
查看源码可以发现Callable接口和Runnable接口一样也是一个函数式接口,可以使用lambda表达式,它的泛型的参数等于返回值,也就是说,我们的参数声明什么格式,call()方法也返回什么样的格式
//源码 @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; } //代码举例 Object class MyThread implements Callable<Object> { @Override public Object call() throws Exception { return null; } } //代码举例 integer class MyThread implements Callable<Integer> { @Override public Integer call() throws Exception { return 1; } } //代码举例 String class MyThread implements Callable<String> { @Override public String call() throws Exception { return "1"; } }
问题:线程一般参数传的都是runnable进行启动(Thread中参数都是runnable),那么如何启动启动callable线程呢?
- 创建对应的线程类
- 通过FutureTask适配类 它的构造器就是callable接口 把callable线程类丢进去 可以把它想成中间商 用它来联系runnable
- 把futureTask丢进去然后就可以执行了
//线程启动的方式尤其只有一个 new Thread().start(); //怎么启动Callable接口线程? 由于Thread类的参数都是runnable类型的 和callable不匹配 //1 new Thread(new Runnable()).start() //2 new Thread(new FutureTask<V>()).start() //3 new Thread(new FutureTask<V>(Callable)).start() //1 创建对应的线程类 MyThread myThread = new MyThread(); //2 通过FutureTask适配类 它的构造器就是callable接口 把callable线程类丢进去 FutureTask futureTask = new FutureTask<>(myThread); //3 把futureTask丢进去然后就可以执行了 new Thread(futureTask, "线程A").start();
代码简单测试Callable接口
package com.wyh.callable; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; /** * @program: JUC * @description: callable接口简单测试 开启线程三种方式之一 * @author: 魏一鹤 * @createDate: 2022-02-17 21:15 **/ //开启线程的三种方式 1继承Thread类 2实现Runnable接口 3实现Callable接口 public class CallableTest { public static void main(String[] args){ //线程启动的方式尤其只有一个 new Thread().start(); //怎么启动Callable接口线程? 由于Thread类的参数都是runnable类型的 和callable不匹配 //1 new Thread(new Runnable()).start() //2 new Thread(new FutureTask<V>()).start() //3 new Thread(new FutureTask<V>(Callable)).start() //实例化静态类 MyThread myThread = new MyThread(); //适配类 它的构造器就是callable接口 可以把它想成中间商 用它来联系runnable FutureTask futureTask = new FutureTask<>(myThread); //把futureTask丢进去然后就可以执行了 new Thread(futureTask, "线程A").start(); } } //静态资源线程类 class MyThread implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println("进入call方法"); //程序员节 10.24 return 1024; } }
完善代码,接收返回值, 使用FutureTask适配器的get()方法接受callable接口的call方法的返回值,需要抛异常,一般返回Object类型,可以根据情况强行转换
package com.wyh.callable; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * @program: JUC * @description: callable接口简单测试 开启线程三种方式之一 * @author: 魏一鹤 * @createDate: 2022-02-17 21:15 **/ //开启线程的三种方式 1继承Thread类 2实现Runnable接口 3实现Callable接口 public class CallableTest { public static void main(String[] args) throws ExecutionException, InterruptedException { //线程启动的方式尤其只有一个 new Thread().start(); //怎么启动Callable接口线程? 由于Thread类的参数都是runnable类型的 和callable不匹配 //1 new Thread(new Runnable()).start() //2 new Thread(new FutureTask<V>()).start() //3 new Thread(new FutureTask<V>(Callable)).start() //实例化静态类 MyThread myThread = new MyThread(); //适配类 它的构造器就是callable接口 可以把它想成中间商 用它来联系runnable FutureTask futureTask = new FutureTask<>(myThread); //把futureTask丢进去然后就可以执行了 new Thread(futureTask, "线程A").start(); //使用futureTask适配器的get方法接收callable接口的返回值 //需要抛异常 //一般object就可以接受各种类型的返回值 //Object o = futureTask.get(); //因为call方法返回的是integer 这里从Object类型强行转换成Integer类型 Integer o = (Integer) futureTask.get(); System.out.println(o); } } //静态资源线程类 class MyThread implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println("进入call方法"); //程序员节 10.24 return 1024; } }
进阶:可能出现的两个问题
问题1:get方法可能阻塞
解决:
1 get方法如果等待返回值时,方法耗时可能会产生阻塞会一直等,所以一般把获取返回结果(get方法)放到最后一行
2 或者通过异步通信(类似ajax)解决 让他先跑,他一边跑我一边执行,等他跑完了我再获取他的返回结果,不用刻意的等待
package com.wyh.callable; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * @program: JUC * @description: callable接口简单测试 开启线程三种方式之一 * @author: 魏一鹤 * @createDate: 2022-02-17 21:15 **/ //开启线程的三种方式 1继承Thread类 2实现Runnable接口 3实现Callable接口 public class CallableTest { public static void main(String[] args) throws ExecutionException, InterruptedException { //线程启动的方式尤其只有一个 new Thread().start(); //怎么启动Callable接口线程? 由于Thread类的参数都是runnable类型的 和callable不匹配 //1 new Thread(new Runnable()).start() //2 new Thread(new FutureTask<V>()).start() //3 new Thread(new FutureTask<V>(Callable)).start() //实例化静态类 MyThread myThread = new MyThread(); //适配类 它的构造器就是callable接口 可以把它想成中间商 用它来联系runnable FutureTask futureTask = new FutureTask<>(myThread); //把futureTask丢进去然后就可以执行了 new Thread(futureTask, "线程A").start(); //使用futureTask适配器的get方法接收callable接口的返回值 //需要抛异常 //一般object就可以接受各种类型的返回值 //Object o = futureTask.get(); //因为call方法返回的是integer 这里从Object类型强行转换成Integer类型 //get方法如果等待返回值时,方法耗时可能会产生阻塞会一直等,所以一般把获取返回结果(get方法)放到最后一行 //或者通过异步通信(类似ajax)解决 让他先跑,他一边跑我一边执行,等他跑完了我再获取他的返回结果,不用刻意的等待 Integer o = (Integer) futureTask.get(); System.out.println(o); } } //静态资源线程类 class MyThread implements Callable<Integer> { @Override public Integer call() throws Exception { //如果这是耗时操作 FutureTask适配器的get()方法就会一直等产生阻塞 System.out.println("进入call方法"); //程序员节 10.24 return 1024; } }