如何使用API​​ java.util.concurrent.Future代替在Java中显式创建线程?

时间:2018-10-28 10:16:21

标签: java multithreading

我有两个线程在Java程序中并行运行,如下所示:

// Threading
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            gpTableCount   = getGpTableCount();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}).start();

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            hiveTableCount = getHiveTableCount();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}).start();

while(!(gpTableCount != null && gpTableCount.size() > 0 && hiveTableCount != null && hiveTableCount.size() > 0)) {
    Thread.sleep(5000);
}
// Threading

它们两者具有相同的功能。下面是来自getHiveTableCount()的代码。另一种方法与下面的方法略有不同(一两行),但是功能保持不变。

public Map<String, String> getHiveTableCount() throws IOException, SQLException {
    hiveDataMap     = new HashMap<String, String>();
    hiveTableErrs   = new HashMap<String, String>();
    Iterator<String> hiveIterator = filteredList.iterator();
    Connection hiveConnection = DbManager.getHiveConnection();
    PreparedStatement hive_pstmnt = null;
    String hiveExcpnMsg;
    String ssn;
    String hiveMaxUpdTms;
    Long hiveCount;
    String gpHiveRec;
    String[] hiveArray;
    String[] hiveDetails;
    String hiveQuery;
    while(hiveIterator.hasNext()) {
        gpHiveRec   = hiveIterator.next();      
        hiveArray   = gpHiveRec.split(",");     
        hiveDetails = hiveArray[1].split("\\.");
        hiveQuery   = "select '" + hiveDetails[1] + "' as TableName, count(*) as Count, source_system_name, max(xx_last_update_tms) from " + hiveArray[1] + " where source_system_name='" + hiveArray[2] + "' group by source_system_name";
        try {
            hive_pstmnt             = hiveConnection.prepareStatement(hiveQuery);
            ResultSet hiveCountRs   = hive_pstmnt.executeQuery();
            while(hiveCountRs.next()) {
                hiveCount     = hiveCountRs.getLong(2);
                ssn           = hiveCountRs.getString(3);
                hiveMaxUpdTms = hiveCountRs.getTimestamp(4).toString();
                hiveDataMap.put(hiveDetails[1] + "," + ssn, hiveCount + "," + hiveMaxUpdTms);
            }
        } catch(org.postgresql.util.PSQLException e) {
            hiveExcpnMsg = e.getMessage();
            hiveTableErrs.put(hiveDetails[1] + ": for the SSN: " + hiveArray[2], hiveExcpnMsg + "\n");
        } catch(SQLException e) {
            hiveExcpnMsg = e.getMessage();
            hiveTableErrs.put(hiveDetails[1] + ": for the SSN: " + hiveArray[2], hiveExcpnMsg + "\n");
        } catch(Exception e) {
            hiveExcpnMsg = e.getMessage();
            hiveTableErrs.put(hiveDetails[1] + ": for the SSN: " + hiveArray[2], hiveExcpnMsg + "\n");
        }
    }
    return hiveDataMap;
}

这两个线程同时运行。我最近在网上阅读了以下内容:

  

未来类代表异步计算的未来结果   –结果将最终出现在将来   处理完成。

我从理论上理解了该概念,但是我不知道如何为上述相同代码应用java.util.concurrent.Future api,而不是显式创建线程。 谁能让我知道如何在方法上实现多线程:getGpTableCount() & getHiveTableCount使用java.util.concurrent.Future api而不是创建线程来创建新线程,例如new Thread(new Runnable()?

3 个答案:

答案 0 :(得分:1)

您正在使用Runnable接口提交任务,该接口不允许线程在计算结束时返回值(并导致您使用共享变量-gpTableCount和{{ 1}})。

hiveTableCount接口是后来的添加,它使您的任务可以返回值(在您的情况下为Callable)。

作为直接使用线程的替代方法,Concurrency API引入了Map<String, String>作为高级对象,该对象管理线程池并能够异步执行任务。

ExecutorService类型的任务提交给Callable时,您期望任务会产生一个值,但是由于提交点和计算的末尾没有耦合,因此{{ 1}}将返回ExecutorService,该值可让您获取此值,如果该值不可用,则阻止。因此,ExecutorService可用于在不同线程之间进行同步。

作为Future的替代方法,您还可以查看Future的实现方式ExecutorService

  

此类提供了FutureTask<V>的基本实现,其中包含启动和取消计算,查询以查看计算是否完成以及检索计算结果的方法

     

FutureTask可用于包装Callable或Runnable对象。

答案 1 :(得分:0)

首先,创建最适合您需求的执行程序服务,例如:

ExecutorService ex = Executors.newFixedThreadPool(2);

(有关执行人的更多信息:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html

然后将Runnable对象更改为Callable,该对象类似于runnable,但是返回一个值(有关callable的更多信息:https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/Callable.html):

Callable<Map<String, String>> callable1 = // your Callable class 

Type参数应与您要作为结果返回的类型相同。

接下来创建您的任务列表:

List<Callable<Map<String, String>>> tasks = new LinkedList<>();
tasks.add(callable1);
tasks.add(callable2);

并执行它们:

Future<Map<String, String>> results = ex.invokeAll(tasks);

以上方法在所有任务完成后返回(如果我正确理解了您的情况,这就是您想要实现的目标),但是完成的任务可能已正常终止或引发异常而终止。

最后关闭执行程序服务:

ex.shutdown();

答案 2 :(得分:0)

如果您使用的是Java 8+,则可以使用CompletableFuture.supplyAsync来简短地表示,例如:

import static java.util.concurrent.CompletableFuture.supplyAsync;
.....
Future<Map<String, String>> f= supplyAsync(()->{
    try{
        return getHiveTableCount();
    } catch(Exception e) {
        throw new RuntimeException(e);
    }
}

CompletableFuture.supplyAsync将在默认情况下使用ForkJoinPool.commonPool()运行它,如果您要使用自己的参数,则还有一个重叠之处,那就是在参数中使用Executor

public class CompletableFuture<T>
extends Object
implements Future<T>, CompletionStage<T>

并且有。

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                   Executor executor)