创建多线程的方式三
我们说过我们创建多线程的方式一共有四种,我们的jdk1.5之后有两种创建多线程的方式,我们的jdk1.5之后有两种创建多线程的方式
- jdk1.5之前的两种分别是继承Thread类,还有就是实现Runnable接口的方式
- jdk1.5开始就有了后两种,分别是实现Callable接口,和使用线程池的方式创建多线程
我们在实际编程中主要是通过使用线程池和的方式创建多线程
使用实现Callable接口创建多线程的步骤:
- 创建一个实现了Callable接口的实现类
- 在实现类中重写Call()方法
- 我们的Call()方法就和我们的run()方法一样,就相当于我们的run()方法的进阶版,我们也需要将我们的这个线程要做的事情写在重写的Call()方法中
- 但是我们的Call()方法可以有返回值,并且我们的Call()方法还可以抛出异常
- 我们的Call()方法就和我们的run()方法一样,就相当于我们的run()方法的进阶版,我们也需要将我们的这个线程要做的事情写在重写的Call()方法中
- 创建实现了Callable接口的实现类的对象
- 创建FutureTask类的对象
- 我们要将实现了Callable接口的实现类对象作为实参传入到我们的FutureTask类的构造方法中
- 创建Thread类的的对象,并且调用start()方法来启动线程
- 我们创建Thread类的对象时要将我们的FutureTask类的对象作为实参传递到我们的Thread类的构造器中来创建Thread类的对象
- 获取Callable接口中的call()方法在实现类中的重写方法的返回值
- 注意:我们这里需要借助FutureTask类来返回结果和创建具体的线程对象
与Runnable接口相比,Callable接口的功能更加强大一些
- 相比于Runable接口中的run()抽象方法,我们的Callable接口中的call()抽象方法可以有返回值
- 并且我们的call()方法可以抛出异常
- 可以抛出异常,那么我们就可以知道这个分线程中出现了什么异常
- 支持泛型的返回值
如何理解实现Callable接口的方式创建的多线程比实现Runnable接口的方式创建的多线程更加强大
- call()方法可以有返回值
- 也就是我们的分线程执行完之后可以给其他的线程返回一个结果
- call()方法可以抛出异常
- 可以抛出异常,也就意味者可以被其他线程捕捉,获取异常的信息
- call()方法是支持泛型的
- 这个是建立在call()方法有返回值的基础上
call()方法在Callable接口中的声明形式为:
- V call() throws Exception();
- 这里的V就是泛型,也就是我们声明的Callable接口的泛型是什么,我们的这个Call()方法的返回值类型也就是什么
- 可以类比我们的Comparable接口以及compareTo()方法
- 这里抛出了一个Exception异常,也就是抛出了一个编译时异常
- 这里的V就是泛型,也就是我们声明的Callable接口的泛型是什么,我们的这个Call()方法的返回值类型也就是什么
如果我们实现类实现的Callable接口是默认泛型,那么这个时候默认为Object类型
注意:
- FutureTask类的实例对象调用get()方法的返回值就是FutureTask类的构造方法的参数Runnable接口实现类重写的call()方法的返回值
- FutureTask类实现了RunnableFuture接口,而我们的RunnableFuture接口又继承了Runable接口和Future接口
- Future接口,可以对具体Runnable和Callable任务的执行结果进行取消,查询知否完成,获取结果等
- FutureTask类是Future接口的唯一的实现类
- FutureTask类同时实现了Runnable接口和我们的Future接口(这里是直接实现了RunnableFuture接口,然后我们的RunnableFuture接口又继承了我们的Runnable 接口和我们的Future接口),这个时候我们的FutureTask接口既可以作为Runnable传入到Thread的构造方法中,也可以作为Future得到Callable中被重写的Call方法的返回值
-
只有当我们的Callable接口的泛型和我们的FutureTask类的泛型相同时,比如都为int类型时,这个时候我们的FutureTask类的对象调用get()方法的返回值才可以是int类型
-
关于泛型我们以后会进行细讲