zoukankan      html  css  js  c++  java
  • java的FutureTask类

    1.FutrueTask概念

    FutureTask一个可取消的异步计算,FutureTask 实现了Future的基本方法,提空 start cancel 操作,可以查询计算是否已经完成,并且可以获取计算的结果。结果只可以在计算完成之后获取,get方法会阻塞当计算没有完成的时候,一旦计算已经完成,那么计算就不能再次启动或是取消。

    一个FutureTask 可以用来包装一个 Callable 或是一个runnable对象。因为FurtureTask实现了Runnable方法,所以一个 FutureTask可以提交(submit)给一个Excutor执行(excution).

    2.FutureTask使用场景

    FutureTask可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。另外,FutureTask还可以确保即使调用了多次run方法,它都只会执行一次Runnable或者Callable任务,或者通过cancel取消FutureTask的执行等。

    2.1 FutureTask执行多任务计算场景

    利用FutureTask和ExecutorService,可以用多线程的方式提交计算任务,主线程继续执行其他任务,当主线程需要子线程的计算结果时,在异步获取子线程的执行结果。

     1 /**
     2  * Created by zhumiao on 2018/8/17.
     3  */
     4 public class FutureTest1 {
     5     public static void main(String[] args) {
     6         Task task = new Task();// 新建异步任务
     7         FutureTask<Integer> future = new FutureTask<Integer>(task) {
     8             // 异步任务执行完成,回调
     9             @Override
    10             protected void done() {
    11                 try {
    12                     System.out.println("future.done():" + get());
    13                 } catch (InterruptedException e) {
    14                     e.printStackTrace();
    15                 } catch (ExecutionException e) {
    16                     e.printStackTrace();
    17                 }
    18             }
    19         };
    20         // 创建线程池(使用了预定义的配置)
    21         ExecutorService executor = Executors.newCachedThreadPool();
    22         executor.execute(future);
    23 
    24         try {
    25             Thread.sleep(1000);
    26         } catch (InterruptedException e1) {
    27             e1.printStackTrace();
    28         }
    29         // 可以取消异步任务
    30 //         future.cancel(true);
    31         try {
    32             // 阻塞,等待异步任务执行完毕-获取异步任务的返回值
    33             System.out.println("future.get():" + future.get());
    34         } catch (InterruptedException e) {
    35             e.printStackTrace();
    36         } catch (ExecutionException e) {
    37             e.printStackTrace();
    38         }
    39     }
    40     // 异步任务
    41     static class Task implements Callable<Integer> {
    42         // 返回异步任务的执行结果
    43         @Override
    44         public Integer call() throws Exception {
    45             int i = 0;
    46             for (; i < 10; i++) {
    47                 try {
    48                     System.out.println(Thread.currentThread().getName() + "_"
    49                             + i);
    50                     Thread.sleep(500);
    51                 } catch (InterruptedException e) {
    52                     e.printStackTrace();
    53                 }
    54             }
    55             return i;
    56         }
    57     }
    58 }
    View Code

    2.2 FutureTask在高并发下确保任务只执行一次

    在很多高并发的环境下,往往我们只需要某些任务只执行一次。这种使用情景FutureTask的特性恰能胜任。举一个例子,假设有一个带key的连接池,当key存在时,即直接返回key对应的对象;当key不存在时,则创建连接。对于这样的应用场景,通常采用的方法为使用一个Map对象来存储key和连接池对应的对应关系,典型的代码如下面所示:

     1 private Map<String, Connection> connectionPool = new HashMap<String, Connection>();  
     2 private ReentrantLock lock = new ReentrantLock();  
     3 
     4 public Connection getConnection(String key){  
     5     try{  
     6         lock.lock();  
     7         if(connectionPool.containsKey(key)){  
     8             return connectionPool.get(key);  
     9         }  
    10         else{  
    11             //创建 Connection  
    12             Connection conn = createConnection();  
    13             connectionPool.put(key, conn);  
    14             return conn;  
    15         }  
    16     }  
    17     finally{  
    18         lock.unlock();  
    19     }  
    20 }  
    21 
    22 //创建Connection(根据业务需求,自定义Connection)  
    23 private Connection createConnection(){  
    24     return null;  
    25 } 
    View Code

    在上面的例子中,我们通过加锁确保高并发环境下的线程安全,也确保了connection只创建一次,然而确牺牲了性能。改用ConcurrentHash的情况下,几乎可以避免加锁的操作,性能大大提高,但是在高并发的情况下有可能出现Connection被创建多次的现象。这时最需要解决的问题就是当key不存在时,创建Connection的动作能放在connectionPool之后执行,这正是FutureTask发挥作用的时机,基于ConcurrentHashMap和FutureTask的改造代码如下:

     1 private ConcurrentHashMap<String,FutureTask<Connection>>connectionPool = new ConcurrentHashMap<String, FutureTask<Connection>>();  
     2 
     3 public Connection getConnection(String key) throws Exception{  
     4     FutureTask<Connection>connectionTask=connectionPool.get(key);  
     5     if(connectionTask!=null){  
     6         return connectionTask.get();  
     7     }  
     8     else{  
     9         Callable<Connection> callable = new Callable<Connection>(){  
    10             @Override  
    11             public Connection call() throws Exception {  
    12                 // TODO Auto-generated method stub  
    13                 return createConnection();  
    14             }  
    15         };  
    16         FutureTask<Connection>newTask = new FutureTask<Connection>(callable);  
    17         connectionTask = connectionPool.putIfAbsent(key, newTask);  
    18         if(connectionTask==null){  
    19             connectionTask = newTask;  
    20             connectionTask.run();  
    21         }  
    22         return connectionTask.get();  
    23     }  
    24 }  
    25 
    26 //创建Connection(根据业务需求,自定义Connection)  
    27 private Connection createConnection(){  
    28     return null;  
    29 } 
    View Code

    经过这样的改造,可以避免由于并发带来的多次创建连接及锁的出现。

    zhumiao
  • 相关阅读:
    LeetCode OJ 112. Path Sum
    LeetCode OJ 226. Invert Binary Tree
    LeetCode OJ 100. Same Tree
    LeetCode OJ 104. Maximum Depth of Binary Tree
    LeetCode OJ 111. Minimum Depth of Binary Tree
    LeetCode OJ 110. Balanced Binary Tree
    apache-jmeter-3.1的简单压力测试使用方法(下载和安装)
    JMeter入门教程
    CentOS6(CentOS7)设置静态IP 并且 能够上网
    分享好文:分享我在阿里8年,是如何一步一步走向架构师的
  • 原文地址:https://www.cnblogs.com/zhumiao/p/9492360.html
Copyright © 2011-2022 走看看