Как асинхронно вызывать метод в Java
Я смотрел Go goroutines в последнее время и подумал, что было бы неплохо иметь что-то подобное в Java. Насколько я обыскал обычный способ распараллеливать вызов метода, нужно сделать что-то вроде:
final String x = "somethingelse";
new Thread(new Runnable() {
public void run() {
x.matches("something");
}
}).start();
Это не очень элегантно. Есть ли лучший способ сделать это? Мне было нужно такое решение в проекте, поэтому я решил реализовать свой собственный класс-оболочку при вызове метода async.
Я опубликовал свой класс-оболочку в 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 вызывать вызовы метода async. Это безопасно? Есть ли более простой способ?
Ответы
Ответ 1
Я только что обнаружил, что есть более чистый способ сделать ваш
new Thread(new Runnable() {
public void run() {
//Do whatever
}
}).start();
(по крайней мере, в Java 8), вы можете использовать лямбда-выражение, чтобы сократить его до:
new Thread(() -> {
//Do whatever
}).start();
Так же просто, как сделать функцию в JS!
Ответ 2
Вы также можете рассмотреть класс java.util.concurrent.FutureTask
.
Если вы используете Java 5 или более позднюю версию, FutureTask - это реализация "подстановочного асинхронного вычисления" под ключ.
В пакете java.util.concurrent
существует еще более богатое асинхронное поведение планирования выполнения (например, ScheduledExecutorService
), но FutureTask
может иметь все необходимые функции.
Я даже зашел так далеко, чтобы сказать, что уже не рекомендуется использовать первый образец кода, который вы дали в качестве примера с тех пор, как FutureTask
стал доступен. (Предполагая, что вы находитесь на Java 5 или новее.)
Ответ 3
Мне не нравится идея использования Reflection для этого.
Не только опасно для его отсутствия в некоторых рефакторингах, но также может быть отказано SecurityManager
.
FutureTask
является хорошим вариантом в качестве других опций из пакета java.util.concurrent.
Мой любимый для простых задач:
Executors.newSingleThreadExecutor().submit(task);
маленький бит короче, чем создание потока (задача - это Callable или Runnable)
Ответ 4
Вы можете использовать @Async
аннотация из jcabi-аспекты и AspectJ:
public class Foo {
@Async
public void save() {
// to be executed in the background
}
}
Когда вы вызываете save()
, начинается новый поток и выполняется его тело. Ваш основной поток продолжается, не дожидаясь результата save()
.
Ответ 5
Для этого вы можете использовать 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/
Ответ 6
Вы можете использовать синтаксис Java8 для CompletableFuture, таким образом вы можете выполнять дополнительные вычисления async на основе результата вызова функции async.
например:
CompletableFuture.supplyAsync(this::findSomeData)
.thenApply(this:: intReturningMethod)
.thenAccept(this::notify);
Более подробную информацию можно найти в этой статье
Ответ 7
На самом деле это не так, но если я должен асинхронно вызывать метод, например. 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();
}
Я тестировал это, и он работает. Просто подумал, что это может помочь другим, если они просто пришли для "асинхронного метода".
Ответ 8
Это, вероятно, не настоящее решение, но теперь - в Java 8 - вы можете сделать этот код, по крайней мере, немного лучше, используя выражение лямбда.
final String x = "somethingelse";
new Thread(() -> {
x.matches("something");
}
).start();
И вы даже можете сделать это в одной строке, все еще имея его довольно читаемым.
new Thread(() -> x.matches("something")).start();
Ответ 9
Вы можете использовать AsyncFunc
из Cactoos:
boolean matches = new AsyncFunc(
x -> x.matches("something")
).apply("The text").get();
Он будет выполнен в фоновом режиме, и результат будет доступен в get()
как Future
.