【问题标题】:How to asynchronously call a method in JavaJava中如何异步调用方法
【发布时间】:2010-12-22 23:59:11
【问题描述】:

我最近一直在看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 的旧版本

标签: java concurrency asynchronous goroutine


【解决方案1】:

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

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 线程自动停止,但是你也可以强制它。
【解决方案2】:

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

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

【讨论】:

  • CompletableFuture 在另一个答案中被提及,但那个答案使用了整个supplyAsync(...) 链。这是一个简单的包装器,非常适合这个问题。
  • ForkJoinPool.commonPool().execute() 的开销略少
【解决方案3】:

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

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

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

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

【讨论】:

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

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

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

    Executors.newSingleThreadExecutor().submit(task);

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

【讨论】:

  • 问题是我只想执行一个方法调用。这样我就不得不改变目标类的实现。我想要的是完全调用,而不必担心会影响 Runnable 或 Callable
  • 那么这不会有太大帮助:(但通常我更喜欢使用 Runnable 或 Callable 而不是 Reflection
  • 简短说明:跟踪ExecutorService 实例会更好,因此您可以在必要时调用shutdown()
  • 如果你这样做了,你会不会得到未关闭的 ExecutorService ,导致你的 JVM 拒绝关闭?我建议您编写自己的方法来获得单线程、有时间限制的 ExecutorService 。
【解决方案5】:

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

例如:

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

更多细节可以在这个article找到

【讨论】:

    【解决方案6】:

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

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

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

    【讨论】:

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

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

    【讨论】:

    • 如果你使用的是 spring-boot。
    【解决方案8】:

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

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

    然后像这样调用函数-

    asyncExecutor.execute(() -> {
    
                            TimeUnit.SECONDS.sleep(3L);}
    

    【讨论】:

      【解决方案9】:

      您可以从Cactoos 使用AsyncFunc

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

      它将在后台执行,结果将以Future 的形式在get() 中提供。

      【讨论】:

        【解决方案10】:

        这不是真的相关,但如果我要异步调用一个方法,例如匹配(),我会使用:

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

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

        【讨论】:

          【解决方案11】:

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

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

          您甚至可以在一行中完成此操作,并且仍然具有很好的可读性。

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

          【讨论】:

          • 在 Java 8 中使用它真的很棒。
          【解决方案12】:

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

          【讨论】:

            猜你喜欢
            • 2011-04-29
            • 1970-01-01
            • 2017-03-06
            • 2017-03-17
            • 1970-01-01
            • 1970-01-01
            • 2016-07-20
            • 1970-01-01
            相关资源
            最近更新 更多