技术交流QQ群【JAVA,C++,Python,.NET,BigData,AI】:170933152

注意之前咱们说的线程池队列中都是用的实现了Runnbale接口的线程,

这里咱们用的是实现了Callable接口的线程.


Callable和Runnbale一样代表着是线程任务,​区别在于Callable有返回值并且可以抛出异常。

创建并启动有返回值的线程的步骤如下:

  1. 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且该call()方法有返回值。
  2. 将Callable实例传入FutureTask类。
  3. 使用FutureTask对象作为Thread对象的target创建并启动新线程。
  4. 调用FutureTask对象的get()方法来获得子线程结束后的返回值

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_返回结果

这里咱们自己通过实现Callable接口来自己弄一个线程.

这个FutureTask这个类是,Callable这种方式实现线程的过程中很重要的一个类

执行线程,需要用这个类

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_线程池_02

执行一下看看,可以看到,因为上面都有2秒延迟,所以延迟后就打印出了一个7

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_线程池_03

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_返回结果_04

可以看到这里00000接着就打印出来了,9是过了2秒打印出来的,也就说,这个延迟是

咱们的线程延迟的.


然后去看一下这个FutureTask这个类去

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_线程池_05

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_线程池_06

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_线程池_07

注意这个类中state是状态,然后可以看到这个状态有

NEW 新建线程

COMPLETING 线程执行完毕

NORMAL 线程就绪

EXCEPTIONAL 线程出现异常

CANCELLED 线程被取消

INTERRUPTING 线程正在被打断

INTERRUPTED 线程被打断.

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_线程池_08

这个callable是用来接收任务的

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_返回结果_09

这个outcome这个是接收返回值的.

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_线程池_10

可以看到FutureTask的构造方法,callable,用来接收任务

然后接收到任务以后给个任务加个状态,是NEW也就是0这个状态

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_死循环_11

然后再看一下FutureTask他的run方法,

可以看到这里的c.call(),其实就是实现callable接口的时候,让实现的那个方法

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_死循环_12

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_返回结果_13

可以看到执行完以后就把callable那个线程的返回值拿到了.

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_死循环_14

拿到以后,走了set(result)

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_死循环_15

把这个返回值赋值给了outcome

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_死循环_16

同时这里,设置结果的时候,就意味着,这个线程执行完了

这个时候就把状态设置为COMPLETING

然后设置返回结果以后,再把这个状态设置为NORMAL,也就是说这个线程又可以为其他任务进行服务了

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_线程池_17

然后,FutureTask,中还有个get()方法,

这个方法,COMPLETING这个是1

NOMARL是2

小于2的时候也就是,线程可能在执行中,这个时候就走

awaitDone进行线程等待

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_线程池_18

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_返回结果_19

可以看到这里走了个死循环,等待什么呢?

这里等待可以看到,这里等待,也就是状态是2,也就是NORMAL的时候

状态是执行完毕的时候,就可以返回线程执行的结果了,否则的话就死循环一直等待.

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_死循环_20

等待执行完以后,再把线程的执行结果返回过去

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_线程池_21

这样就实现了了,对线程执行的等待,也就是,当调用FutureTask的get()方法的时候,会等

线程执行完毕以后,再返回执行结果


好了,有了上面的对Callable的理解,接下来我们用线程池,来执行一下,实现了

callable的线程任务

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_线程池_22

可以看到每个callable线程执行以后,最后我都会去get,获取执行结果,要注意

这里执行任务不是用的execute,这里用的submit,并且,执行这个任务以后,返回的是个

Future对象.


执行可以看到结果,因为我们队列大小是3,并且,这里,call中,sleep了2秒钟

所以,执行一下的结果就是,每当2秒以后,就会打印出一行信息,因为

每执行一个任务这里都会get一下.get的时候,就会等待线程执行完以后,也就是2秒以后,才会返回执行结果.

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_返回结果_23

这样太慢了对吧,这样咱们可以这样

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_线程池_24

用个list,吧执行后返回的Future都记录下来,然后统一打印

JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务_返回结果_25

这个时候,可以看到,会3个,3个的打印出来,这个是怎么回事呢?

是这样的,因为这里咱们线程池队列大小有3个,也就是同时可以有3个线程去执行任务,

这样同时会执行3个任务,也就是同时会有3个输出,也就是,同时会有3个任务在那里等待2秒,这样输出的时候

就是3个,3个一块输出的了.