Future 的理解

197 阅读4分钟

Future的理解

我今天看future的时候,有很多都是这么写的,如下

他说future的核心思想是主线程需要等待,我们用了future后,可以不需要等待去执行别的任务,还配了个图。

我不清楚大家如何去理解的,如果我第一次看到这个,我可能会产生一个误区就是多线程之间是同步进行的,也就是说下面这个代码中,只有先执行了hellowold1,才能去执行helloworld2

public static void main(String[] args) {
    // thread1 线程
   new Thread(() -> {
      try {
         Thread.sleep(10000);
         System.out.println("hello world1");
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }, Thread1);
   System.out.println("hello world2");
}

然而并不是,他这里所说的异步指的是,在某一个特定需求下,我们必须要先执行完helloworld1,才能去执行helloworld2,比如,线程thread1 与主线程具有前后依赖关系。这个时候如果是普通线程的写法,如果这样写肯定是异步的,会不符合实际情况。所以我们需要join一下,保证先后顺序。

好了到这就很清楚了,有了前提条件,我们再去理解future的好处。 futrue更像是“中间变量”, 用来关系线程的操作结果,监控线程。 我们普通线程想要保证这个前提,可以join就保证了顺序,但是也导致了时间变长了。更像一个黑盒,输入的值是知道的,输出的值是知道的,然后又变成下一个黑盒的输入。我们为了保证下一个黑盒的输入不是上一个的中间变量而是最后结果,只能这样去等。

而有了future就不一样了,它更像是去在线程加了一个监控者,他知道这个线程执没执行完,到没到最后结果。就像Main大哥,派了个小弟去管,自己去做别的了。

public static void main(String[] args) {
   // callable
   FutureTask futureTask = new FutureTask(() -> {
      try {
         Thread.sleep(10000);
         System.out.println("hello world1");
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      return "helloworld1";
   });

   // runnable
   FutureTask futureTask2 = new FutureTask(() -> {
      try {
         Thread.sleep(10000);
         System.out.println("hello world1");
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   },"ok");

   System.out.println("hello world2");
}

上面是两个写法,一个里面放的是callable,返回的是计算后要返回的结果放到futuretask, 如果是runnable,因为无返回值所以是返回个固定状态码就可以了,比如“ok"。

可是我们还是没有满足最开始定的需求,我们怎么用它呢? 用futureTask.get() Future 有5个基本的方法, get是获取这个线程执行后内容,就是获取上面我们的helloworld1或者ok,他是阻塞方法,我们调用get,如果这个线程没完成就会阻塞在这一直等待返回的结果。

boolean cancel(boolean mayInterruptIfRunning);

boolean isCancelled();

boolean isDone();

V get() throws InterruptedException, ExecutionException;

V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException;

又有疑问了,那他和join 也没区别啊,都是一样的时间啊,不都是需要阻塞等待吗。这就又回到了future异步的概念,我们在阻塞之前的代码是可以执行的,只有在需要的时候才去等待,看代码把。

public static void main(String[] args) throws InterruptedException, ExecutionException {
      // callable
      FutureTask futureTask = new FutureTask(() -> {
         try {
            Thread.sleep(1000);
            System.out.println("hello world1");
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      },"ok");

      // 下面两行的代码是不会阻塞不执行的,
      TimeUnit.HOURS.sleep(1);
      System.out.println("做了一些事情,花费了一小时");

      // 到这会等待上面的futureTask执行完成,可是我们上面花费一小时远远大于1000秒,所以这就          相当于节省了1000秒
      if (futureTask.get().equals("ok")) {
         System.out.println("hello world2");
      }
   }

   public static void main(String[] args) throws InterruptedException {
      Thread thread1 = new Thread(() -> {
         try {
            Thread.sleep(1000);
            System.out.println("hello world1");
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      });thread1.start();


      // 我们无法判断上面的线程thread1什么时候执行完,只能在这加个join
      thread1.join();

      TimeUnit.HOURS.sleep(1);
      System.out.println("做了一些事情,花费了一小时");
      // if就可以不要了,因为是顺序的,但是时间多花费了1000秒
      // if (futureTask.get().equals("ok")) {
         System.out.println("hello world2");
      // }

   }

TimeUnit.HOURS.sleep(1);

System.out.println("做了一些事情,花费了一小时");

这两段代码的执行我们不没有任何等待。 省时间就省在了我们可以掌握线程状态,即拿即用,就算等待也是最小等待时间。

总结:

  1. Future 异步特性从何而来,有他就可以相当于派出小兵,可以知道每个线程的执行状态和返回结果
  2. get() 阻塞等待返回结果,即拿即用,阻塞前一切异步执行。