ChatGPT解决这个技术问题 Extra ChatGPT

Java中如何异步调用方法

我最近一直在看 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 中进行这样的异步方法调用的主题提出一些意见。安全吗?已经有更简单的方法了吗?

你能再次展示你的 J-Go lib 代码吗?
我正在做一个项目,我正在阅读流字符。一旦阅读了一个完整的单词,我就会对该单词执行许多操作。最后我把它放到了一个集合中。读取流中的所有数据后,我将返回响应。我决定每次对一个单词执行操作时都启动线程。但它降低了整体性能。然后我才知道线程本身就是一项昂贵的操作。我不确定启动一个线程并发调用一个方法是否可以增加任何性能,直到它执行任何繁重的 IO 操作。
好问题!! Java 8 为此提供了 CompletableFutures。其他答案可能基于 Java 的旧版本

M
MartinTeeVarga

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

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

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

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

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


您的回答帮助了我的问题 - stackoverflow.com/questions/27009448/…。应用我的情况有点棘手,但最终解决了:)
如何带参数调用?
@yatanadam 这可能会回答您的问题。只需将上面的代码放在一个方法中,然后像往常一样传递变量。看看我为您制作的test code
@eNnillaMS 线程运行后是否必须停止?它是自动停止还是由垃圾收集器停止?
@user3004449 线程自动停止,但是,您也可以强制它。
R
Rahul Chauhan

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

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

另一个答案中提到了 CompletableFuture,但该答案使用了整个 supplyAsync(...) 链。这是一个简单的包装器,非常适合这个问题。
ForkJoinPool.commonPool().execute() 的开销略少
s
shadit

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

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

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

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


问题是我只想执行一个方法调用。这样我就不得不改变目标类的实现。我想要的是完全调用,而不必担心会影响 Runnable 或 Callable
我听到你了。 Java(还)没有一流的函数,所以这是目前最先进的。
感谢“未来”关键字...现在我正在打开有关它们的教程...非常有用。 :D
-1 据我所知,FutureTask 本身不足以异步运行任何东西。您仍然需要创建一个 Thread 或 Executor 来运行它,就像 Carlos 的示例一样。
B
Buhake Sindi

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

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

    Executors.newSingleThreadExecutor().submit(task);

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


问题是我只想执行一个方法调用。这样我就不得不改变目标类的实现。我想要的是完全调用,而不必担心会影响 Runnable 或 Callable
那么这不会有太大帮助:(但通常我更喜欢使用 Runnable 或 Callable 而不是 Reflection
只是一个简短的说明:跟踪 ExecutorService 实例会更好,因此您可以在必要时调用 shutdown()
如果你这样做了,你会不会得到未关闭的 ExecutorService ,导致你的 JVM 拒绝关闭?我建议您编写自己的方法来获得单线程、有时间限制的 ExecutorService 。
@djangofan 对。此外,在极少数情况下,此代码可能会抛出 RejectedExecutionException 拒绝运行任务 (JDK-8145304)。
T
Tal Avissar

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

例如:

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

可在此 article 中找到更多详细信息


y
yegor256

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

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

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


请注意,这种方法会使所有调用都异步保存,这可能是我们想要的,也可能不是我们想要的。如果仅对给定方法的某些调用应该异步进行,则可以使用此页面上建议的其他方法。
我可以在 Web 应用程序中使用它吗(因为不建议在那里管理线程)?
u
user3177227

您可以为此使用 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/


如果您使用的是弹簧引导。
A
Anand

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

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

然后像这样调用函数-

asyncExecutor.execute(() -> {

                        TimeUnit.SECONDS.sleep(3L);}

1.) 您分配一个具有多个线程的线程池,而您只需要一个。 2.) 你没有关闭你的执行者。每个执行器都应该正确关闭,否则它的线程将永远运行。
y
yegor256

您可以使用 Cactoos 中的 AsyncFunc

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

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


k
kcpr

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

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

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

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

使用 Java 8 使用它真的很棒。
F
FlameBlazer

这并不真正相关,但如果我要异步调用一个方法,例如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();
}

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


v
vitro

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