如何在Java中异步调用方法

Fel*_*mel 108 java concurrency asynchronous goroutine

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

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

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

我在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
Run Code Online (Sandbox Code Playgroud)

或者更简洁:

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

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

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

小智 140

我刚刚发现有一种更清洁的方法可以做到

new Thread(new Runnable() {
    public void run() {
        //Do whatever
    }
}).start();
Run Code Online (Sandbox Code Playgroud)

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

new Thread(() -> {
    //Do whatever
}).start();
Run Code Online (Sandbox Code Playgroud)

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

  • @yatanadam 这可能会回答您的问题。只需将上面的代码放在一个方法中,然后像往常一样传递变量。看看我为你制作的这个[测试代码](https://pastebin.com/8f1Tmscy)。 (2认同)

Rah*_*han 40

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

CompletableFuture.runAsync(() -> {
    // method call or code to be asynch.
});
Run Code Online (Sandbox Code Playgroud)

  • `ForkJoinPool.commonPool().execute()` 的开销稍微少一些 (2认同)

sha*_*dit 31

您也可以考虑上课java.util.concurrent.FutureTask.

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

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

我甚至会说,自从FutureTask可用之后再使用您提供的第一个代码模式不再可取.(假设您使用的是Java 5或更高版本.)

  • -1据我所知,FutureTask本身不足以异步运行任何东西.你仍然需要创建一个Thread或Executor来运行它,就像在Carlos的例子中一样. (4认同)

use*_*421 23

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

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

    Executors.newSingleThreadExecutor().submit(task);
Run Code Online (Sandbox Code Playgroud)

比创建一个Thread 稍微短一点(task是Callable或Runnable)

  • 简单说明:跟踪`ExecutorService`实例更好,所以你可以在必要时调用`shutdown()`. (4认同)
  • 如果你这样做,你是否可能会得到未关闭的 ExecutorService ,导致你的 JVM 拒绝关闭?我建议您编写自己的方法来获得单线程、限时的 ExecutorService 。 (2认同)

Tal*_*sar 13

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

例如:

 CompletableFuture.supplyAsync(this::findSomeData)
                     .thenApply(this:: intReturningMethod)
                     .thenAccept(this::notify);
Run Code Online (Sandbox Code Playgroud)

可以在本文中找到更多详细信息


yeg*_*256 10

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

public class Foo {
  @Async
  public void save() {
    // to be executed in the background
  }
}
Run Code Online (Sandbox Code Playgroud)

当你调用时save(),一个新线程启动并执行它的主体.你的主线程继续而不等待结果save().


小智 8

您可以使用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);
}
Run Code Online (Sandbox Code Playgroud)

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

  • 如果你使用弹簧靴. (7认同)

小智 7

Java 还提供了一种调用异步方法的好方法。在java.util.concurrent我们有ExecutorService帮助做同样的事情。像这样初始化你的对象 -

 private ExecutorService asyncExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
Run Code Online (Sandbox Code Playgroud)

然后调用函数,如 -

asyncExecutor.execute(() -> {

                        TimeUnit.SECONDS.sleep(3L);}
Run Code Online (Sandbox Code Playgroud)