如何在Java中异步调用方法


126

我最近一直在研究Go的goroutine,并且认为在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的类,并进行了一些反射工作以获取正确的方法对象并对其进行调用。

我希望对我的小型库以及在Java中进行这样的异步方法调用的问题有一些看法。安全吗?已经有一个简单的方法了吗?


1
您可以再次显示J-Go lib的代码吗?
msangel 2013年

我当时正在做一个项目,在这个项目中我阅读了流式处理。读完一个完整的单词后,我将对该单词执行许多操作。最后,我将其放入一个集合中。读取流中的所有数据后,我将返回响应。我决定每次对一个单词执行操作时都启动线程。但是它降低了整体性能。然后我才知道线程本身是一项昂贵的操作。我不确定启动线程并发调用方法是否可以增加性能,直到执行大量的IO操作为止。
阿米特·库玛·古普塔

2
好问题!!Java 8为此提供了CompletableFutures。其他答案基于可能是Java的旧版本
程序员

Answers:


155

我只是发现有一种更清洁的方法

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

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

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

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


您的回答解决了我的问题-stackoverflow.com/questions/27009448/…。应用我的情况有点棘手,但最终解决了问题:)
Deckard 2014年

如何调用参数?
yatanadam '16

2
@yatanadam这可能会回答您的问题。只需将上面的代码放入方法中,然后像平常一样粘贴变量即可。看看我为您制作的此测试代码
user3004449

1
@eNnillaMS运行后是否必须停止线程?它是自动停止还是由垃圾收集器停止?
user3004449

@ user3004449线程自动停止,但是您也可以强制执行该操作。
肖恩

58

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

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

CompletableFuture在另一个答案中被提及,但是一个人使用了整个supplyAsync(...)链。这是一个简单的包装程序,非常适合该问题。
ndm13'2

ForkJoinPool.commonPool().execute()的开销较小
Mark Jeronimus

31

您不妨也考虑上课java.util.concurrent.FutureTask

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

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

我什至可以说,自从您使用第一个代码模式作为示例以来,FutureTask就不再建议这样做。(假设您使用的是Java 5或更高版本。)


事情是我只想执行一个方法调用。这样,我将不得不更改目标类的实现。我想要的东西就是完全调用,而不必担心增加Runnable或Callable的问题
Felipe Hummel,2009年

我听到你了 Java还没有一流的功能,因此这是目前的最新水平。
shadit

感谢您提供“未来”关键字...现在,我正在打开有关它们的教程...非常有用。:D
gumuruh

4
据我所知,-1本身不足以异步运行任何东西。您仍然需要创建一个线程或执行程序来运行它,如Carlos的示例所示。
MikeFHay

24

我不喜欢使用反射的想法。
不仅在某些重构中丢失它很危险,而且还可以通过拒绝它SecurityManager

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

    Executors.newSingleThreadExecutor().submit(task);

位比创建一个线程(任务是可赎回或一个Runnable)短


1
事情是我只想执行一个方法调用。这样,我将不得不更改目标类的实现。我想要的就是完全调用,而不必担心增加Runnable或Callable
Felipe Hummel,2009年

那么这将无济于事:(但是通常我更喜欢使用Runnable或Callable而不是Reflection
user85421

4
简短说明:更好地跟踪ExecutorService实例,因此您可以在需要shutdown()时致电。
Daniel Szalay 2012年

1
如果这样做,您是否可能会以未关闭的ExecutorService结尾,从而导致JVM拒绝关闭?我建议您编写自己的方法以获得单线程,有时间限制的ExecutorService。
djangofan

14

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

例如:

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

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


10

您可以@Asyncjcabi-aspects和AspectJ 使用注释:

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

当您调用时save(),新线程将启动并执行其主体。您的主线程将继续运行,而无需等待的结果save()


1
请注意,这种方法将使所有保存异步的调用成为可能,而不是我们想要的。如果仅应异步调用给定方法,则可以使用此页面上建议的其他方法。
bmorin

我可以在Web应用程序中使用它吗(因为在那里不建议管理线程)?
onlinenaman

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);
}

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


10
如果您使用的是spring-boot。
乔瓦尼·托拉尔多

6

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

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

然后像下面这样调用函数

asyncExecutor.execute(() -> {

                        TimeUnit.SECONDS.sleep(3L);}

3

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

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

您甚至可以在一行中做到这一点,但仍保持可读性。

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

这是非常好的使用该使用Java 8
解脱

3

您可以AsyncFuncCactoos使用:

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

它会在后台执行,其结果将是可用get()Future


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();
}

我已经对此进行了测试,并且可以正常工作。只是认为如果其他人只是为“异步方法”而来可能会对他们有所帮助。


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);
            });
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.