• FutureTask——另一种闭锁的实现


    关于多线程,我们接触对多的,最基础,入门的可能就是实现Runnable接口继承Thead类,因为Java单继承的原因,通常建议是实现Runnable接口。但这种“简单”的线程会带来一个问题,写过的人都知道,不管是实现Runnable还是继承Thread类,他们都是实现public void run()方法,而这个方法可以看到是没有返回值的。但我们想执行一个线程得到它的返回值应该怎么做呢?此时FutureTask就要出场了。

    首先我们还是需要定义一个线程,不同的是这个线程需要实现Callable接口。这是第一个不同的地方。

     1 package futuretask;
     2 
     3 import java.util.concurrent.Callable;
     4 
     5 /**
     6  * Created by yulinfeng on 12/17/16.
     7  */
     8 public class Task implements Callable<String> {
     9     private String name;
    10 
    11     public Task(String name){
    12         this.name = name;
    13     }
    14 
    15     @Override
    16     public String call() throws Exception {
    17         String hello = hello(name);
    18         return hello;
    19     }
    20 
    21     public String hello(String name){
    22         return "hello " + name;
    23     }
    24 }

    可以看到此时并不是实现run方法,而是一个叫call的方法,同时有返回值(返回值取决于所定义的泛型)。定义好这个线程过后,并不是直接将此线程的实例传入一个Thread类中执行start方法,这也是和没有返回值的线程不同的地方。

    下面我们通过两种方法使用FutureTask,一个是通过线程池的方式创建线程,一个是直接创建一个线程。


    通过多线程的方式创建线程:

     1 package futuretask;
     2 
     3 import countdownlatch.TaskThread;
     4 
     5 import java.util.concurrent.*;
     6 
     7 /**
     8  * Created by yulinfeng on 12/17/16.
     9  */
    10 public class Test {
    11     public static void main(String[] args){
    12         ExecutorService exec = Executors.newCachedThreadPool();     //创建一个线程池
    13         Future<String> hello = exec.submit(new Task("kevin"));  //执行线程池的submit方法,执行callable线程,并返回一个Future对象
    14         try {
    15             System.out.println(hello.get());    //通过调用Future的get方法,获取线程的返回值
    16         } catch (InterruptedException e) {
    17             e.printStackTrace();
    18         } catch (ExecutionException e) {
    19             e.printStackTrace();
    20         }
    21     }
    22 }

    这是第一种通过线程池创建线程的方式,通过调用线程的submit方法。如果是实现的Runnable方法,通过线程池创建线程执行的则是线程池的execute方法。一定注意调用线程返回值时使用的是Future的get方法来获取的返回值。


    直接创建线程:

     1 package futuretask;
     2 
     3 import countdownlatch.TaskThread;
     4 
     5 import java.util.concurrent.*;
     6 
     7 /**
     8  * Created by yulinfeng on 12/17/16.
     9  */
    10 public class Test {
    11     public static void main(String[] args){
    12         FutureTask<String> future = new FutureTask<String>(new Task("kevin"));  //将线程传入FuturaTask的构造方法中
    13         Thread thread = new Thread(future); 
    14         thread.start();
    15         try {
    16             System.out.println(future.get());
    17         } catch (InterruptedException e) {
    18             e.printStackTrace();
    19         } catch (ExecutionException e) {
    20             e.printStackTrace();
    21         }
    22     }
    23 }

    在调用get方法时,一定是在执行完线程后,不然将会一直阻塞。

    其实仔细观察以上两张方法,第一种是Future接口,第二种是FutureTask类。FutureTask是Future接口的实现,为什么不能将Future接口传入Thread的构造方法中呢?原因很简单。在Thread的单个参数构造方法中,只能传入Runnable类型,而Future并没有继承Runnable接口,FutureTask则同时继承了Runnable接口和Future接口。

    最后,那么FutureTask应该怎么来定义呢?其实和我们上一节将的CountDownLatch一样,它也是一种闭锁的实现。Future.get取决于任务的状态,如果任务已完成,那么get会立即返回结果,否则get将阻塞直到任务进入完成状态,然后返回结果或者抛出异常。FutureTask将计算结果从执行计算的线程传递到获取这个结果的线程,而FutureTask的规范确保了这种传递过程能实现结果的安全发布。——《Java并发编程实践》

  • 相关阅读:
    (原)torch中threads的addjob函数使用方法
    (原)torch中提示Unwritable object <userdata> at <?>.callback.self.XXX.threads.__gc__
    (原)luarocks更新某个模块
    EL表达式
    leetcode 151反转单词
    括号生成
    leetcode 机器人能到达的位置
    leetcode 翻转数组
    leetcode 460 LFU缓存
    leetcode 42 接雨水
  • 原文地址:https://www.cnblogs.com/yulinfeng/p/6193659.html
Copyright © 2020-2023  润新知