江的福的小本本之Callable

下面是Thread 的构造函数,我们发现它并没有定义以Callable为参数的有参构造。
在这里插入图片描述
但是,如果我们想用实现Callable 的方法,来开启线程,应该怎么办呢?

在这里插入图片描述
如上图,能进Thread的构造方法是一个Runnable接口的实现,但此时我们只有一个Callable接口,这时候我们就需要找一个“中间人”。 也就是说,如果我们能找到一个类,同时实现了Runnable接口和Callable接口,那么我们是不是只需要往Thread 中传这一个类,就能够解决这个问题。这个就叫做“适配器模式”。
来看API
在这里插入图片描述
Runnable 的所有已知的子接口中,有RunnableFututre以及RunnableScheduledFuture两个接口。然后来看看RunnableFututre接口。
在这里插入图片描述
RunnableFututre是Runnable接口的子接口,我们来看看它的实现类FutureTask。
在这里插入图片描述
我们看到,RunnableFututre接口的实现类FutureTask是实现了Runnable接口的。在这里插入图片描述
并且FutureTask的构造注入可以丢进来一个Callable接口。
所以现在的情况就是这个FutureTask不仅实现了Runnable接口,并且构造注入可以丢进去一个Callable接口,这样FutureTask可以作为Runnable和Callable的“中间人”。在这里插入图片描述
在这里插入图片描述
这样就实现Callable接口就可以作为一种获取多线程的方式。

那么我们再来思考,为什么会出现Callable呢?它肯定不是平白无故就从石头缝里蹦出来的。

第一点:实现Callable可以有返回值,返回值可以用get方法调用。

第二点:在这里插入图片描述
这是一个main线程执行任务的过程,即顺序执行。我们可以看到,由上往下,第三个任务耗时很久,这就出现了阻塞的现象,但是如果我们能在执行到第三个任务时,就再新开一个线程A,让A去之后执行第三个任务,然后main线程跳过任务三,直接执行任务四,这样是不是阻塞问题就迎刃而解了呢。FutureTask“未来的任务”就做到了这个问题的解决。
这里还有一个需要注意的问题,那就是之前第一点提到的那个get方法,get方法如无必要,是建议将它放到最后的。因为get方法是要求获得Callable的线程的计算结果的,如果计算没有完成,就会等它直到计算完成。在这里插入图片描述
这样的话,岂不是就相当于第二点解决的阻塞问题,又产生了?所以需要将调用get,放在末尾。

第三点:当多个线程工用一个futureTask时,只会有一个线程进入实现Callable的类的call方法,其余线程只是复用返回值,而无需再调用。
如下

FutureTask<Integer> futureTask = new FutureTask<>(new CallableDemo1());
        //FutureTask<Integer> futureTask1 = new FutureTask<>(new CallableDemo1());

        Thread thread = new Thread(futureTask, "aaa");
        Thread thread1 = new Thread(futureTask, "bbb");

        thread.start();
        thread1.start();
        System.out.println(futureTask.get());

结果

aaa*******come in*********
109

发布了51 篇原创文章 · 获赞 11 · 访问量 1769

猜你喜欢

转载自blog.csdn.net/weixin_45276914/article/details/105338996