我最近一直在看 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 的类并做一些反射工作来获取正确的方法对象并调用它。
我想对我的小型库以及在 Java 中进行这样的异步方法调用的主题提出一些意见。安全吗?已经有更简单的方法了吗?
我刚刚发现有一种更清洁的方法来做你的
new Thread(new Runnable() {
public void run() {
//Do whatever
}
}).start();
(至少在 Java 8 中),您可以使用 lambda 表达式将其缩短为:
new Thread(() -> {
//Do whatever
}).start();
就像在 JS 中做一个函数一样简单!
Java 8 在包 java.util.concurrent.CompletableFuture 中引入了 CompletableFuture,可用于进行异步调用:
CompletableFuture.runAsync(() -> {
// method call or code to be asynch.
});
CompletableFuture
,但该答案使用了整个 supplyAsync(...)
链。这是一个简单的包装器,非常适合这个问题。
ForkJoinPool.commonPool().execute()
的开销略少
您可能还希望考虑类 java.util.concurrent.FutureTask
。
如果您使用的是 Java 5 或更高版本,FutureTask 是“可取消的异步计算”的统包实现。
java.util.concurrent
包中提供了更丰富的异步执行调度行为(例如,ScheduledExecutorService
),但 FutureTask
可能具有您需要的所有功能。
我什至会说,自从 FutureTask
可用以来,不再建议使用您提供的第一个 Code Pattern 作为示例。 (假设您使用的是 Java 5 或更高版本。)
我不喜欢为此使用反射的想法。
在某些重构中丢失它不仅很危险,而且还可能被 SecurityManager
拒绝。
与 java.util.concurrent 包中的其他选项一样,FutureTask
是一个不错的选择。
我最喜欢的简单任务:
Executors.newSingleThreadExecutor().submit(task);
比创建线程短一点(任务是 Callable 或 Runnable)
ExecutorService
实例会更好,因此您可以在必要时调用 shutdown()
。
您可以对 CompletableFuture 使用 Java8 语法,这样您就可以根据调用异步函数的结果执行额外的异步计算。
例如:
CompletableFuture.supplyAsync(this::findSomeData)
.thenApply(this:: intReturningMethod)
.thenAccept(this::notify);
可在此 article 中找到更多详细信息
您可以使用来自 jcabi-aspects 和 AspectJ 的 @Async
注释:
public class Foo {
@Async
public void save() {
// to be executed in the background
}
}
当您调用 save()
时,会启动一个新线程并执行其主体。您的主线程继续运行,无需等待 save()
的结果。
您可以为此使用 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/
Java 还提供了一种调用异步方法的好方法。在 java.util.concurrent 我们有 ExecutorService 可以帮助做同样的事情。像这样初始化你的对象 -
private ExecutorService asyncExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
然后像这样调用函数-
asyncExecutor.execute(() -> {
TimeUnit.SECONDS.sleep(3L);}
这可能不是一个真正的解决方案,但现在 - 在 Java 8 中 - 您可以使用 lambda 表达式使这段代码看起来至少更好一点。
final String x = "somethingelse";
new Thread(() -> {
x.matches("something");
}
).start();
你甚至可以在一行中做到这一点,仍然具有很好的可读性。
new Thread(() -> x.matches("something")).start();
这并不真正相关,但如果我要异步调用一个方法,例如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();
}
我已经对此进行了测试,并且可以正常工作。只是认为如果他们只是为了“异步方法”而来,它可能会对其他人有所帮助。
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);
});
}
}
不定期副业成功案例分享