并发学习计划-FutureTask一个强大的类03

在学习FutureTask之前,我们应该先了解一个接口——RunnableFuture

这个接口是什么呢,给大家看一下

public interface RunnableFuture extends Runnable, Future {
  void run();
}

这是一个很简单的接口吧。

继承了Runnable和Future类,runnable就不说了,runnable也是一个接口,并且也有一个run方法。
我们平时用runnable都是和Thread一起使用的。所以runnable就是执行的内容,到底要运行啥,我们自己来决定。不是有个待实现的run方法吗。

Future是什么呢,我在《ExecutorService和Future的关系》这篇文章有讲到这个东西。

这个东西大概就是可以得到运行结果的一个东西,当然他不仅仅可以得到运行结果,还可以中断任务。

所以RunnableFuture同时继承了Runnable和Future,也就是说。

RunnableFuture不但知道要运行什么,而且还有中断运行的能力。

不过RunnableFuture也只是一个接口而已,只是定义了这个接口应该干啥。如果我们非要在取消的方法里面写下载一张图片的代码,还不是可以。

闲话少说,RunnableFuture定义了一个东西,这个东西可以运行任务,还可以中断任务。

不过也只是定义而已,我们来看看RunnableFuture的具体实现类——FutureTask。

终于讲到正题了哈。

FutureTask的源码很少,不到500行,不过源码写的很厉害,但不推荐阅读。因为里面也牵扯了部分我们不认识的类。

既然FutureTask继承了RunnableFuture,并且是具体的实现类,说明我们可以直接使用这个东西了。

并且可以指定一个任务,要运行什么,而且也可以中断任务,所以这是一个很方便的类呢。

学习这个类的方法推荐一种,直接使用。

这个类继承Runnable,所以我们可以把他当runnable使用。runnable我们平时怎么使用的。

举个简单的例子:

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("hi runnable");
    }
};
new Thread(runnable).start();

所以,FutureTask也可以这样使用,举个例子。

FutureTask futureTask = new FutureTask(new Callable() {
    @Override
    public String call() throws Exception {
        System.out.println("hi futureTask");
        return null;
    }
});
new Thread(futureTask).start();

感觉还是有点区别,还记得之前说过的吗,FutureTask不仅仅可以执行任务,还可以得到结果,所以这个类不是有一个泛型可以泛指结果吗。

既然有结果,在运行的过程中返回一个结果很合理吧。

这个结果可以是任何东西,我们可以举一个实际开发中会遇到的问题。——下载一张图片
例子:

FutureTask futureTask = new FutureTask(new Callable() {
    @Override
    public String call() throws Exception {
        for (int i = 1; i <= 10; i++) {
            Thread.sleep(500);
            System.out.println("下载中:" + (i / 10.0 * 100) + "%" + "(假装耗时)");
        }
        return "图片";
    }
});

new Thread(futureTask).start();

try {
    System.out.println(futureTask.get());
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}

代码有点多了,看完看完。很简单的。

模拟下载一张图片,最后得到这种图片,所以运行结果是这样的。


下载中:10.0%(假装耗时)
下载中:20.0%(假装耗时)
下载中:30.0%(假装耗时)
下载中:40.0%(假装耗时)
下载中:50.0%(假装耗时)
下载中:60.0%(假装耗时)
下载中:70.0%(假装耗时)
下载中:80.0%(假装耗时)
下载中:90.0%(假装耗时)
下载中:100.0%(假装耗时)
图片

看完代码有没有什么疑问呢?

应该有疑问才对哈。

用线程下载这张图片,但是是在主线程直接打印,难道不会报空指针吗,应该还没有下载完毕就执行打印语句了啊,应该空指针啊。

这就是FutureTask的厉害之处,如果调用get方法,如果没有get到,会阻塞在get方法那里,直到get到为止。

我们多加两行代码,看看是否如此。

 long startTime = System.currentTimeMillis();
 System.out.println(futureTask.get());
 System.out.println("耗时:" + (System.currentTimeMillis() - startTime));

很简单的耗时判断吧?

运行结果:

图片
耗时:5047

所以会一直阻塞在那里,之前我在讲《ArrayBlockingQueue和LinkedBlockingQueue》的时候,有讲过这种阻塞,大概就是这种阻塞。

FutureTask可以被打断还记得吗,我们可以使用cancel(boolean mayInterruptIfRunning)来打断任务。

参数的含义是,如果被打断了,是否还要继续执行下去。

感觉这个类讲的差不多了。

what a nice class!

你可能感兴趣的:(并发学习计划-FutureTask一个强大的类03)