Как асинхронно вызывать метод в Java - PullRequest
92 голосов
/ 03 декабря 2009

В последнее время я смотрел на goroutines Go и думал, что было бы неплохо иметь что-то подобное в 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 и выполняет некоторую работу Reflection, чтобы получить правильный объект метода и вызвать его.

Мне нужно мнение о моей крошечной библиотеке и о том, как делать вызовы асинхронных методов в Java. Это безопасно? Есть ли уже более простой способ?

Ответы [ 11 ]

131 голосов
/ 09 июня 2014

Я только что обнаружил, что есть более чистый способ сделать

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

(по крайней мере в Java 8), вы можете использовать лямбда-выражение, чтобы сократить его до:

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

Так же просто, как сделать функцию в JS!

31 голосов
/ 04 декабря 2009

Вы можете также рассмотреть класс java.util.concurrent.FutureTask.

Если вы используете Java 5 или более позднюю версию, FutureTask - это готовая реализация «отменяемого асинхронного вычисления».

В пакете java.util.concurrent (например, ScheduledExecutorService) доступны еще более расширенные возможности планирования асинхронного выполнения, но FutureTask может иметь все необходимые вам функции.

Я бы даже сказал, что больше не рекомендуется использовать первый кодовый шаблон, который вы дали в качестве примера, с тех пор, как стал доступен FutureTask. (Предполагается, что вы находитесь на Java 5 или более поздней версии.)

23 голосов
/ 25 января 2018

Java 8 представила CompletableFuture, доступную в пакете java.util.concurrent.CompletableFuture, может использоваться для выполнения асинхронного вызова:

CompletableFuture.runAsync(() -> {
    // method call or code to be asynch.
});
21 голосов
/ 04 декабря 2009

Мне не нравится идея использовать Reflection для этого.
Не только опасно пропустить его в каком-то рефакторинге, но также может быть отклонено SecurityManager.

FutureTask - хороший вариант, как и другие опции из пакета java.util.concurrent.
Мой любимый для простых заданий:

    Executors.newSingleThreadExecutor().submit(task);

немного немного короче, чем создание потока (задача является вызываемой или выполняемой)

9 голосов
/ 01 сентября 2017

Вы можете использовать синтаксис Java8 для CompletableFuture, таким образом, вы можете выполнять дополнительные асинхронные вычисления на основе результата вызова асинхронной функции.

например:

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

Более подробную информацию можно найти в этой статье

9 голосов
/ 05 июня 2014

Вы можете использовать @Async аннотацию из jcabi-aspect и AspectJ:

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

Когда вы вызываете save(), новый поток запускается и выполняет свое тело. Ваш основной поток продолжается, не дожидаясь результата save().

6 голосов
/ 23 июня 2015

Вы можете использовать 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/

2 голосов
/ 10 июля 2017

Вы можете использовать AsyncFunc из Cactoos :

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

Он будет выполнен на заднем плане, а результат будет доступен в get() как Future.

2 голосов
/ 01 ноября 2015

Возможно, это не настоящее решение, но сейчас - в Java 8 - вы можете сделать этот код, по крайней мере, немного лучше, используя лямбда-выражения.

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

И вы даже можете сделать это в одну строку, при этом все еще довольно читабельно.

new Thread(() -> x.matches("something")).start();
2 голосов
/ 25 октября 2014

Это на самом деле не связано, но если бы я должен был вызвать метод асинхронно, например match (), я бы использовал:

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

Я проверил это, и оно работает. Просто подумал, что это может помочь другим, если они просто пришли за «асинхронным методом».

...