153

I've been looking at Go's goroutines lately and thought it would be nice to have something similar in Java. As far as I've searched the common way to parallelize a method call is to do something like:

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

Thats not very elegant. Is there a better way of doing this? I needed such a solution in a project so I decided to implement my own wrapper class around a async method call.

I published my wrapper class in J-Go. But I don't know if it is a good solution. The usage is simple:

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

or less verbose:

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

Internally I'm using a class that implements Runnable and do some Reflection work to get the correct method object and invoking it.

I want some opinion about my tiny library and on the subject of making async method calls like this in Java. Is it safe? Is there already a simplier way?

4

12 回答 12

183

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

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

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

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

就像在 JS 中做一个函数一样简单!

于 2014-06-08T20:08:04.263 回答
95

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

CompletableFuture.runAsync(() -> {
    // method call or code to be asynch.
});
于 2018-01-25T13:35:05.847 回答
34

你不妨也考虑类java.util.concurrent.FutureTask

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

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

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

于 2009-12-03T21:31:55.527 回答
24

我不喜欢为此使用反射的想法。
在某些重构中丢失它不仅很危险,而且它也可以被SecurityManager.

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

    Executors.newSingleThreadExecutor().submit(task);

比创建线程短一点(任务是 Callable 或 Runnable)

于 2009-12-03T22:32:46.633 回答
19

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

例如:

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

更多细节可以在这篇文章中找到

于 2017-09-01T08:50:31.553 回答
11

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

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

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

于 2014-06-05T05:25:11.027 回答
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/

于 2015-06-23T14:15:56.120 回答
8

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

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

然后像这样调用函数-

asyncExecutor.execute(() -> {

                        TimeUnit.SECONDS.sleep(3L);}
于 2019-03-26T05:44:21.420 回答
3

您可以AsyncFuncCactoos使用:

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

get()它将在后台执行,结果将以Future.

于 2017-07-10T09:05:00.513 回答
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();
}

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

于 2014-10-25T11:04:12.860 回答
2

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

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

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

new Thread(() -> x.matches("something")).start();
于 2015-10-31T21:36:32.310 回答
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);
            });
    }
}
于 2019-11-08T16:04:11.880 回答