上一章我们讲到了实现多线程的两种方式,继承Thread类和实现Runnable接口。
	在日常工作中,我们建议使用实现Runnable接口的方式,因为可以避免单线程局限和更好的体现出数据共享(多个线程访问同一资源)的概念。
	这一章我们讲述多线程的第三种实现方式及一些常用操作方法。
复制代码

上一章链接: 两种实现多线程的方式(详细~~~)

使用Callable接口实现多线程

使用Runnable接口实现的多线程已经很好了,但是有一个"缺点"。

为什么把这个"缺点"打上引号呢?
因为run()方法作为线程的一个主方法,有的人认为主方法没必要有返回值了,就如我们的主入口main()函数一样。
但有的人又认为它跟main()函数不一样,他们认为run()方法不是整个程序的起点,应该有返回值。
所以这是否是缺点则仁者见仁智者见智了
复制代码

所以为了解决这种矛盾,有返回值的Callable接口就应运而生了。不过实现起来较其它两种稍微有点麻烦。接下来我们深入探究。

  • 首先,前两种实现方法都来自java.lang包,但是Callable接口在java.util.concurrent包下。
  • 而且它也是一个函数式接口,有个call()方法,有个返回值,返回值类型为Callable接口中的泛型决定

范例:

  • 我们先实现一个Callable接口的线程类,并写点测试代码。



我们是想线程运行完会返回一个 “票已卖光!”的字符串。好,测试代码写好,我们开始启动线程。

接下来才是重头戏。

  • 上一章说过,启动多线程一定要依靠Thread类,这次也不例外,但是我们来看看Thread类的构造器。

但是并没有看到Thread类接收一个Callable接口的构造,那么我们怎么启动呢?

  • 别急,我们再去java.util.concurrent包下看看。



从JDK1.5开始,提供有FutureTask类,这个类主要负责对Callable接口对象操作,所以我们再来观察下这个类的定义结构是啥样的。



  • 这个类实现了一个RunnableFuture接口,看起来跟Runnbale接口好像有点关系,我们再去看看。
  • 我们看到了啥,果然跟Runnable接口有关系,RunnableFuture接口是它的子类!还有一个Future接口,我们也看看这是个啥。



  • Future接口中有几个方法,我们关注下get()方法,后面会使用到它获取返回值。
通过以上的分析,我们越来越接近启动Callable线程的方法了,现在我们再回到前面说的用来操作Callable接口的FutureTask类。
复制代码



  • 我们看到FutureTask的构造能接收Callable接口对象!这样我们就可以启动了!
  • 可能有的同学逻辑还不是很清楚,我再来梳理一遍。
首先,我们知道线程启动一定要依靠Thread类,但是Thread类只能接收Runnbale接口对象。
	然后我们根据定义接口一层一层找。
	发现有个FutureTask类能接收Callable接口对象,而且它还是RunnableFuture接口的实现类,而RunnableFuture接口又是Runnable接口的子接口,那么我们就可以用Thread类的构造器来接收它了。
	至此我们就可以启动实现Callable接口的线程了。
复制代码



到现在,我们才把第三种实现多线程的方式走通,确实稍微有点麻烦,设计的也有点复杂。现在我们稍微总结一下

  • 对于多线程的实现,通用的还是使用Runnable接口,Callable比较少用到。不过在特定环境需要用到线程的返回值,那Callable就当仁不让了。