如何在Java中异步调用方法

时间:2009-12-03 20:34:39

标签: java concurrency asynchronous goroutine

我最近一直在关注Go's goroutines,并认为在Java中有类似的东西会很好。据我所知,并行化方法调用的常用方法是执行以下操作:

final String x = "somethingelse";
new Thread(new Runnable() {
           public void run() {
                x.matches("something");             
    }
}).start();

这不是很优雅。 有更好的方法吗?我在项目中需要这样的解决方案,所以我决定围绕异步方法调用实现自己的包装类。

我在J-Go发布了我的包装类。但我不知道这是不是一个好的解决方案。用法很简单:

SampleClass obj = ...
FutureResult<Integer> res = ...
Go go = new Go(obj);
go.callLater(res, "intReturningMethod", 10);         //10 is a Integer method parameter
//... Do something else
//...
System.out.println("Result: "+res.get());           //Blocks until intReturningMethod returns

或更简洁:

Go.with(obj).callLater("myRandomMethod");
//... Go away
if (Go.lastResult().isReady())                //Blocks until myRandomMethod has ended
    System.out.println("Method is finished!");

在内部,我正在使用一个实现Runnable的类,并做一些Reflection工作来获取正确的方法对象并调用它。

我想对我的小型库以及在Java中进行这样的异步方法调用的主题有所了解。安全吗?是否有更简单的方法?

12 个答案:

答案 0 :(得分:128)

我刚刚发现有一种更清洁的方式来做你的

new Thread(new Runnable() {
    public void run() {
        //Do whatever
    }
}).start();

(至少在Java 8中),您可以使用lambda表达式将其缩短为:

new Thread(() -> {
    //Do whatever
}).start();

就像在JS中创建函数一样简单!

答案 1 :(得分:31)

您可能还希望考虑课程java.util.concurrent.FutureTask

如果您使用的是Java 5或更高版本,FutureTask是“可取消异步计算”的交钥匙实现。

java.util.concurrent包中有更丰富的异步执行调度行为(例如,ScheduledExecutorService),但FutureTask可能具有您需要的所有功能。

我甚至会说,自FutureTask可用以来,不再使用您提供的第一个代码模式作为示例。 (假设您使用的是Java 5或更高版本。)

答案 2 :(得分:21)

我不喜欢使用Reflection的想法 在某些重构中不仅有丢失危险,而且SecurityManager也可以拒绝它。

FutureTask是java.util.concurrent包中的其他选项的一个很好的选择 我最喜欢的简单任务:

    Executors.newSingleThreadExecutor().submit(task);

比创建一个Thread(任务是Callable或Runnable)短一点

答案 3 :(得分:21)

Java 8在程序包java.util.concurrent.CompletableFuture中引入了CompletableFuture,可用于进行异步调用:

CompletableFuture.runAsync(() -> {
    // method call or code to be asynch.
});

答案 4 :(得分:9)

您可以使用@Async和AspectJ中的jcabi-aspects注释:

public class Foo {
  @Async
  public void save() {
    // to be executed in the background
  }
}

当您致电save()时,新线程将启动并执行其正文。主线程继续而不等待save()的结果。

答案 5 :(得分:9)

您可以使用CompletableFuture的Java8语法,这样您就可以根据调用异步函数的结果执行其他异步计算。

例如:

 CompletableFuture.supplyAsync(this::findSomeData)
                     .thenApply(this:: intReturningMethod)
                     .thenAccept(this::notify);

可在此article

中找到更多详情

答案 6 :(得分:6)

您可以使用Future-AsyncResult。

@Async
public Future<Page> findPage(String page) throws InterruptedException {
    System.out.println("Looking up " + page);
    Page results = restTemplate.getForObject("http://graph.facebook.com/" + page, Page.class);
    Thread.sleep(1000L);
    return new AsyncResult<Page>(results);
}

参考:https://spring.io/guides/gs/async-method/

答案 7 :(得分:2)

这不是真正相关的,但如果我要异步调用一个方法,例如matches(),我会用:

private final static ExecutorService service = Executors.newFixedThreadPool(10);
public static Future<Boolean> matches(final String x, final String y) {
    return service.submit(new Callable<Boolean>() {

        @Override
        public Boolean call() throws Exception {
            return x.matches(y);
        }

    });
}

然后调用我将使用的异步方法:

String x = "somethingelse";
try {
    System.out.println("Matches: "+matches(x, "something").get());
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}

我测试了这个并且它有效。如果他们只是为了“异步方法”而想到它可能会有所帮助。

答案 8 :(得分:2)

这可能不是一个真正的解决方案,但现在 - 在Java 8中 - 您可以使用lambda表达式使这些代码看起来更好一些。

final String x = "somethingelse";
new Thread(() -> {
        x.matches("something");             
    }
).start();

你甚至可以在一行中做到这一点,仍然具有很好的可读性。

new Thread(() -> x.matches("something")).start();

答案 9 :(得分:2)

您可以使用AsyncFunc中的Cactoos

boolean matches = new AsyncFunc(
  x -> x.matches("something")
).apply("The text").get();

它将在后台执行,结果将作为get()Future中提供。

答案 10 :(得分:1)

EA还为Async-Await创建了一个不错的库:https://github.com/electronicarts/ea-async

摘自自述文件:

使用EA异步

import static com.ea.async.Async.await;
import static java.util.concurrent.CompletableFuture.completedFuture;

public class Store
{
    public CompletableFuture<Boolean> buyItem(String itemTypeId, int cost)
    {
        if(!await(bank.decrement(cost))) {
            return completedFuture(false);
        }
        await(inventory.giveItem(itemTypeId));
        return completedFuture(true);
    }
}

没有EA异步

import static java.util.concurrent.CompletableFuture.completedFuture;

public class Store
{
    public CompletableFuture<Boolean> buyItem(String itemTypeId, int cost)
    {
        return bank.decrement(cost)
            .thenCompose(result -> {
                if(!result) {
                    return completedFuture(false);
                }
                return inventory.giveItem(itemTypeId).thenApply(res -> true);
            });
    }
}

答案 11 :(得分:0)

Java还提供了一种调用异步方法的好方法。在 java.util.concurrent 中,我们有 ExecutorService 可以帮助您做到这一点。像这样初始化您的对象-

 private ExecutorService asyncExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

然后调用函数,如-

asyncExecutor.execute(() -> {

                        TimeUnit.SECONDS.sleep(3L);}