ChatGPT解决这个技术问题 Extra ChatGPT

How to asynchronously call a method in Java

I've been looking at Go's goroutines lately and thought it would be nice to have something similar in Java. As far as I've searched the common way to parallelize a method call is to do something like:

final String x = "somethingelse";
new Thread(new Runnable() {
           public void run() {
                x.matches("something");             
    }
}).start();

Thats not very elegant. Is there a better way of doing this? I needed such a solution in a project so I decided to implement my own wrapper class around a async method call.

I published my wrapper class in J-Go. But I don't know if it is a good solution. The usage is simple:

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

or less verbose:

Go.with(obj).callLater("myRandomMethod");
//... Go away
if (Go.lastResult().isReady())                //Blocks until myRandomMethod has ended
    System.out.println("Method is finished!");

Internally I'm using a class that implements Runnable and do some Reflection work to get the correct method object and invoking it.

I want some opinion about my tiny library and on the subject of making async method calls like this in Java. Is it safe? Is there already a simplier way?

Can you show your code of J-Go lib again?
I was working on a project where I was reading a stream char wise. Once a complete word is read I was performing many operations on that word. Finally I put it into a collection. Once the all data from the stream is read I return the response. I decided to start thread for each time I perform operation on a word. But it decreased the overall performance. Then I got to know that threads are in itself are an expensive operation. I am not sure if starting a thread to call a method concurrently can give any performance add until it is performing any heavy IO operation.
Great question!! Java 8 provides CompletableFutures for this. Other answers are based on probably old versions of Java

M
MartinTeeVarga

I just discovered that there is a cleaner way to do your

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

(At least in Java 8), you can use a lambda expression to shorten it to:

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

As simple as making a function in JS!


Your answer helped my problem - stackoverflow.com/questions/27009448/…. Little tricky to apply my situatioin, but worked it out eventually :)
How to call with parameters ?
@yatanadam This may answer your question. Just place the above code inside a method and pas the variable like you would normally do. Check out this test code I made for you.
@eNnillaMS Does the thread has to be stopped after running? Does it stop automatically, or by the garbage collector?
@user3004449 The thread stops automatically, however, you can force it as well.
R
Rahul Chauhan

Java 8 introduced CompletableFuture available in package java.util.concurrent.CompletableFuture, can be used to make a asynch call :

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

CompletableFuture was mentioned in another answer, but that one used the whole supplyAsync(...) chain. This is a simple wrapper that fits the question perfectly.
ForkJoinPool.commonPool().execute() has slightly less overhead
s
shadit

You may wish to also consider the class java.util.concurrent.FutureTask.

If you are using Java 5 or later, FutureTask is a turnkey implementation of "A cancellable asynchronous computation."

There are even richer asynchronous execution scheduling behaviors available in the java.util.concurrent package (for example, ScheduledExecutorService), but FutureTask may have all the functionality you require.

I would even go so far as to say that it is no longer advisable to use the first code pattern you gave as an example ever since FutureTask became available. (Assuming you are on Java 5 or later.)


The thing is I just want to execute one method call. This way I would have to change the implementation of the target class. The thing I wanted is exactly call without having to worry about impĺementing Runnable or Callable
I hear you. Java does not (yet) have first-class functions, so this is the state of the art right now.
thanks for the 'future' keywords... now I'm opening the tutorials about them... very useful. :D
-1 as far as I can tell, FutureTask by itself is not sufficient to run anything asynchronously. You still need to create a Thread or Executor to run it, as in Carlos' example.
B
Buhake Sindi

i don't like the idea of using Reflection for that.
Not only dangerous for missing it in some refactoring, but it can also be denied by SecurityManager.

FutureTask is a good option as the other options from the java.util.concurrent package.
My favorite for simple tasks:

    Executors.newSingleThreadExecutor().submit(task);

little bit shorter than creating a Thread (task is a Callable or a Runnable)


The thing is I just want to execute one method call. This way I would have to change the implementation of the target class. The thing I wanted is exactly call without having to worry about impĺementing Runnable or Callable
then this would'nt help much :( but normally I would prefer using a Runnable or Callable instead of Reflection
Just a short note: It's nicer to keep track of the ExecutorService instance, so you can call shutdown() when you have to.
If you did this, wouldn't you possibly end up with un-closed ExecutorService , causing your JVM to refuse to shutdown? I would recommend writing your own method to get a single-threaded, time-limited, ExecutorService .
@djangofan Right. Also, in rare cases this code can throw RejectedExecutionException refusing to run the task (JDK-8145304).
T
Tal Avissar

You can use the Java8 syntax for CompletableFuture, this way you can perform additional async computations based on the result from calling an async function.

for example:

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

More details can be found in this article


y
yegor256

You can use @Async annotation from jcabi-aspects and AspectJ:

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

When you call save(), a new thread starts and executes its body. Your main thread continues without waiting for the result of save().


Note that this approach would make all calls to save asynchronous, which might or might not be what we want. In cases when only some calls to a given method should be made async, other approaches suggested on this page can be used.
Can I use it in a web application (since managing threads is not recommended there) ?
u
user3177227

You can use Future-AsyncResult for this.

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

Reference: https://spring.io/guides/gs/async-method/


if you are using spring-boot.
A
Anand

Java also provides a nice way of calling async methods. in java.util.concurrent we have ExecutorService that helps in doing the same. Initialize your object like this -

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

and then call the function like-

asyncExecutor.execute(() -> {

                        TimeUnit.SECONDS.sleep(3L);}

1.) You allocate a thread pool with multiple threads while you only need one. 2.) You don't shut down your executor. Every executor should be properly shut down, otherwise its threads will run forever.
y
yegor256

You can use AsyncFunc from Cactoos:

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

It will be executed at the background and the result will be available in get() as a Future.


k
kcpr

It's probably not a real solution, but now - in Java 8 - You can make this code look at least a little better using lambda expression.

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

And You could even do this in one line, still having it pretty readable.

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

It's really nice to use this using Java 8.
F
FlameBlazer

This is not really related but if I was to asynchronously call a method e.g. matches(), I would use:

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

    });
}

Then to call the asynchronous method I would use:

String x = "somethingelse";
try {
    System.out.println("Matches: "+matches(x, "something").get());
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}

I have tested this and it works. Just thought it may help others if they just came for the "asynchronous method".


v
vitro

There is also nice library for Async-Await created by EA: https://github.com/electronicarts/ea-async

From their Readme:

With EA Async

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

Without EA Async

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