171

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?

Felipe Hummel
  • 4,674
  • 5
  • 32
  • 35
  • 1
    Can you show your code of J-Go lib again? – msangel Sep 11 '13 at 19:00
  • 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. – Amit Kumar Gupta Apr 03 '16 at 12:59
  • 2
    Great question!! Java 8 provides CompletableFutures for this. Other answers are based on probably old versions of Java – TriCore Sep 12 '17 at 18:57

12 Answers12

197

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!

MartinTeeVarga
  • 10,478
  • 12
  • 61
  • 98
AegisHexad
  • 2,164
  • 1
  • 11
  • 11
  • Your answer helped my problem - http://stackoverflow.com/questions/27009448/prevent-client-repeatedly-sending-requests. Little tricky to apply my situatioin, but worked it out eventually :) – Deckard Nov 19 '14 at 06:56
  • How to call with parameters ? – yatanadam Dec 29 '16 at 22:04
  • 2
    @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](https://pastebin.com/8f1Tmscy) I made for you. – user3004449 Apr 12 '17 at 07:00
  • 1
    @eNnillaMS Does the thread has to be stopped after running? Does it stop automatically, or by the **garbage collector**? – user3004449 Apr 12 '17 at 08:07
  • @user3004449 The thread stops automatically, however, you can force it as well. – Shawn Jun 04 '18 at 21:25
  • @Deckard - how was this tricky for you? I am asking as I have the exact same question as you have asked in your link. – mrtechmaker Oct 05 '18 at 08:30
  • @AegisHexad for an application this will spawn as many threads as possible, which will impact the memory hence it might increase the latency, if there is a very small task to execute within thread then this would work, but if we are performing some blocking operation(network calls, etc), then multiple threads being spawned can choke up the app, right? – Amit Upadhyay Oct 27 '20 at 02:56
  • Is there a way to end this thread after the process is completed? – Ananya Apr 18 '23 at 23:03
125

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.
});
Rahul Chauhan
  • 1,487
  • 1
  • 11
  • 13
  • `CompletableFuture` was mentioned in another answer, but that one used the whole `supplyAsync(...)` chain. This is a simple wrapper that fits the question perfectly. – ndm13 Feb 05 '18 at 15:20
  • 2
    `ForkJoinPool.commonPool().execute()` has slightly less overhead – Mark Jeronimus Aug 28 '18 at 11:02
34

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.)

shadit
  • 2,536
  • 1
  • 16
  • 21
  • 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 – Felipe Hummel Dec 04 '09 at 02:45
  • I hear you. Java does not (yet) have first-class functions, so this is the state of the art right now. – shadit Dec 04 '09 at 05:11
  • thanks for the 'future' keywords... now I'm opening the tutorials about them... very useful. :D – gumuruh Jun 07 '12 at 08:50
  • 5
    -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. – MikeFHay Jul 10 '13 at 10:21
25

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)

Buhake Sindi
  • 87,898
  • 29
  • 167
  • 228
user85421
  • 28,957
  • 10
  • 64
  • 87
  • 1
    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 – Felipe Hummel Dec 04 '09 at 02:44
  • then this would'nt help much :( but *normally* I would prefer using a Runnable or Callable instead of Reflection – user85421 Dec 04 '09 at 12:43
  • 5
    Just a short note: It's nicer to keep track of the `ExecutorService` instance, so you can call `shutdown()` when you have to. – Daniel Szalay Jun 12 '12 at 09:36
  • 2
    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 Aug 22 '17 at 17:32
  • @djangofan Right. Also, in rare cases this code can throw RejectedExecutionException refusing to run the task ([JDK-8145304](https://bugs.openjdk.org/browse/JDK-8145304)). – ZhekaKozlov Jul 17 '22 at 19:54
23

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

Tal Avissar
  • 10,088
  • 6
  • 45
  • 70
14

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().

yegor256
  • 102,010
  • 123
  • 446
  • 597
  • 1
    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. – bmorin Oct 22 '15 at 12:19
  • 1
    Can I use it in a web application (since managing threads is not recommended there) ? – Naman Jul 05 '17 at 15:12
8

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/

user3177227
  • 315
  • 3
  • 3
8

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);}
Anand
  • 97
  • 1
  • 2
  • 7
  • 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. – ZhekaKozlov Jul 17 '22 at 19:32
3

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.

yegor256
  • 102,010
  • 123
  • 446
  • 597
2

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();
kcpr
  • 1,055
  • 1
  • 12
  • 28
1

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".

FlameBlazer
  • 1,592
  • 2
  • 10
  • 9
1

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);
            });
    }
}
vitro
  • 900
  • 6
  • 20