java如何获取多线程的返回值?

        想要获取多线程的返回值,开启线程的方式就只能选择使用实现Callable接口的方式了,同实现Runnable接口的方式不同,实现Callable的方式有两大优点,第一个是能够获取到线程任务异步执行的结果,第二个是,当任务执行过程中发生了异常时,这个异常不止能够在任务内部处理,还可以向上抛出,也就是抛到call方法的外部来处理。

        Callable接口同Runnable接口一样,也是一个函数式接口,它内部只定义了一个抽象方法call方法,我们要在call方法内实现任务的具体业务逻辑;Callable接口定义了泛型,当实现Callable接口时,我们要为它指定泛型的具体类型,call方法是有返回值的,返回值的具体类型与接口上定义的泛型类型相同;call方法能够将执行过程中发生的异常向外抛出,所以它的方法声明上也声明了throws Exeception。java提供了Future接口和它的实现类FutureTask来协助我们实现跨线程式地获取Callable任务的执行结果,当我们的业务需求是需要获取线程任务的执行结果时,我们首先要创建一个Callable接口的实现类,或者是通过匿名内部类的方式来定义call方法的具体逻辑,然后创建一个Callable对象,将这个Callable对象作为构造参数传给FutureTask的构造器来构造一个FutureTask对象,再将这个FutureTask对象作为构造参数传给Thread类的构造器构造一个Thread对象,然后调用这个Thread对象的start方法去启动这个任务线程,最后就可以通过调用FutureTask对象的get方法来获取线程任务异步执行的结果了。

那么FutureTask对象的get方法是如果实现跨线程获取Callable任务的执行结果的呢?

        要实现这个功能,get方法首先要做的操作就是让调用它的线程进入阻塞状态,调用线程只有进入了阻塞状态才能等待任务线程执行完毕,才能去获取执行结果,否则可能任务线程还没执行完成而调用线程就执行完成退出了。所以在FutureTask类中定义了一个链表类型的成员变量WaitNode waiters,专门用于存放因为调用此FutureTask对象的get方法而进入阻塞状态的线程。

FutureTask是如何执行Callable任务的呢?

        FutureTask这个类除了实现了Future接口外,还实现了Runnable接口,这也是它能够作为Thread类的构造参数的原因,而Runnable接口的run方法即为线程的线程体,所以FutureTask对象所实现的run方法也就是任务线程的线程体,当任务线程被cpu调度到,进入running状态时,执行的就是FutureTask对象的run方法,而在FutureTask对象的run方法内部其实执行的是构造此FutureTask对象的那个Callable对象的call方法,也就是我们定义的任务的业务逻辑,当call方法执行完毕,就产生了任务结果,而这个结果就是那些调用线程所要获取的结果,FutureTask对象为了让那些线程获取到这个结果,所以设计了另外一个Object类型的成员变量outcome,用于保存call方法的执行结果,所以在拿到call方法的执行结果后,run方法会将结果赋值给这个outcome变量,赋值完成之后,run方法所要执行的下一步操作就是唤醒链表中阻塞着的线程了,它会遍历链表,将所有因为调用了get方法而进入阻塞状态的线程唤醒,当这些线程被唤醒之后,会去继续get方法的执行,也就是去FutureTask对象的outcome变量中获取任务执行结果,至此,就实现了任务结果的跨线程获取,也即实现了多线程返回值的获取。

        当然我们在实际的开发中,并不会用这种new Thread创建野生线程的方式来进行多线程编程,我们一般会用线程池来管理线程,线程池ExecutorService提供了submit方法用于往池中提交任务,这个方法有一个Future类型的返回值,我们可以使用这个方法来提交一个Callable或者Runnable类型的任务到线程池中,然后通过它返回的Future对象调用get方法来获取到任务的异步执行的结果,原理同上面讲的一样。所以当我们有获取多线程任务执行结果的业务需求时,要使用submit方法来提交任务而不是使用execute方法,因为execute方法没有返回值,我们也就无法通过返回对象来获取任务结果了。

你可能感兴趣的:(java,开发语言)