I have some legacy, non-RX code that does some networking work by spawning a new Thread.
When the work finishes, it invokes one method on a callback.
I don't have control over the thread this code runs on. It's legacy, and it spawns a new Thread by itself.
This can be simplified in something like:
interface Callback {
void onSuccess();
}
static void executeRequest(String name, Callback callback) {
new Thread(() -> {
try {
System.out.println(" Starting... " + name);
Thread.sleep(2000);
System.out.println(" Finishing... " + name);
callback.onSuccess();
} catch (InterruptedException ignored) {}
}).start();
}
I want to convert this to an RX Completable. To do so I use Completable#create().
The implementation of the CompletableEmitter calls the executeRequest passing of implementation of the Callback that signals when the request has finished.
I also print a log trace when subscribed to help me debugging.
static Completable createRequestCompletable(String name) {
return Completable.create(e -> executeRequest(name, e::onComplete))
.doOnSubscribe(d -> System.out.println("Subscribed to " + name));
}
This works as expected. The Completable completes only after the "request" finishes and the callback is invoked.
Problem is that when subscribing to this these completables in a trampoline scheduler, it does not wait for the first request to finish before subscribing to the second request.
This code:
final Completable c1 = createRequestCompletable("1");
c1.subscribeOn(Schedulers.trampoline()).subscribe();
final Completable c2 = createRequestCompletable("2");
c2.subscribeOn(Schedulers.trampoline()).subscribe();
Outputs:
Subscribed to 1
Starting... 1
Subscribed to 2
Starting... 2
Finishing... 1
Finishing... 2
As you see, it subscribes to the second Completable before the first Completable has completed, even if I'm subscribing in trampoline.
I'd like to queue the completables, so that the second waits for the first to finish, outputting this:
Subscribed to 1
Starting... 1
Finishing... 1
Subscribed to 2
Starting... 2
Finishing... 2
I'm sure the problem is related to the work being done in a worker thread. If the implementation of the Completable does not spawn a new thread it works as expected.
But this is legacy code and what I'm trying to do is adapting it to RX without modifying.
NOTE: the requests are executed in different points of the program - I cannot use andThen or concat to implement the serialised execution.
I have managed to execute the Completables sequentially by explicitly blocking the subscription Thread with a Latch.
But I don't think this is the idiomatic way to do it in RX, and I still don't understand why I need to do this and the Thread is not blocked until the Completable completes.
static Completable createRequestCompletable(String name) {
final CountDownLatch latch = new CountDownLatch(1);
return Completable.create(e -> {
executeRequest(name, () -> {
e.onComplete();
latch.countDown();
});
latch.await();
})
.doOnSubscribe(disposable -> System.out.println("Subscribed to " + name));
}
Related
// assume: serviceCall1 throws an exception after 1s, servserviceCall2 runs 10s without exception
CompletableFuture<String> serviceCall1Future = serviceCall1.execute();
CompletableFuture<String> serviceCall2Future = serviceCall2.execute();
CompletableFuture<Void> allOffFuture = CompletableFuture.allOf(serviceCall1Future, serviceCall2Future);
// does not work, will be called after thread 2 has finished
allOffFuture.exceptionally( ex -> { allOffFuture.cancel(true); return null; } );
try {
// waiting for threads to finish
allOffFuture.join();
} catch (CompletionException e) {
// does not work, here we come after thread 2 has finished
allOffFuture.cancel(true);
}
If one thread throws an exception, in my case it doesnt make any sense for the other thread(s) to keep on running, so I want them both (all in case of more than 2 threads) to stop . How can I achieve that ?
I guess something like this should work:
CompletableFuture<String> serviceCall1Future = serviceCall1.execute();
CompletableFuture<String> serviceCall2Future = serviceCall2.execute();
CompletableFuture<String> foo1 = serviceCall1Future.whenComplete((result,exception) -> {if(exception != null) serviceCall2Future.cancel(true);});
CompletableFuture<String> foo2 = serviceCall2Future.whenComplete((result,exception) -> {if(exception != null) serviceCall1Future.cancel(true);});
CompletableFuture<Void> allOffFuture = CompletableFuture.allOf(foo1, foo2);
// ... rest of your code
This cancels the other future when the one completes with an exception.
If you are using an ExecutorService with CompletableFuture, you can use Shutdowns methods like shutdown() or shutdownNow().
If you want to shut down the ExecutorService immediately, you can call the shutdownNow() method. This will attempt to stop all executing tasks right away, and skips all submitted but non-processed tasks. There are no guarantees given about the executing tasks. Perhaps they stop, perhaps the execute until the end. It is a best effort attempt. Here is an example of calling ExecutorService shutdownNow()
See -> https://jenkov.com/tutorials/java-util-concurrent/executorservice.html#executorservice-shutdown
I'm learning Reactive programming with project-reactor.
I have the following test case:
#Test
public void createAFlux_just() {
Flux<String> fruitFlux = Flux.just("apple", "orange");
fruitFlux.subscribe(f -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(f);
});
System.out.println("hello main thread");
}
By executing the test it seems that the main thread is stuck for 5 seconds.
I would expect that the subscribed consumer should run asynchronously in its own thread, that is, the subscribe invoke should return immediately to the main thread and consequently the hello main thread should print instantly.
The main thread is stuck because the subscription happens on the main thread. If you want it to run asynchronously, you need to the subscription to happen on a thread other than main. You could do this as:
#Test
public void createAFlux_just() {
Flux<String> fruitFlux = Flux.just("apple", "orange");
fruitFlux.subscribeOn(Schedulers.parallel()).subscribe(f -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(f);
});
System.out.println("hello main thread");
}
Note: I have used the parallel thread pool. You could use whatever pool you like. Reactor's pipelines are executed on the calling thread by default (unlike CompletableFuture<T> which runs in the ForkJoin pool by default).
This behavior would be the case if you had an observable (Flux) that was asynchronous. You chose to use a Flux with two readily available values by using the just method. They were passed to the subscription object right away since they were immediately available.
from spring.io documentation
The Threading Model
Reactor operators generally are concurrent agnostic: they don’t impose a particular threading model and just run on the Thread on which their onNext method was invoked.
The Scheduler abstraction
In Reactor, a Scheduler is an abstraction that gives the user control about threading. A Scheduler can spawn Worker which are conceptually Threads, but are not necessarily backed by a Thread (we’ll see an example of that later). A Scheduler also includes the notion of a clock, whereas the Worker is purely about scheduling tasks.
so you should subscribe on different thread by subscribeOn method and the Thread.sleep(5000) will sleep thread of the scheduler. You can see more examples like this one in the documentation.
Flux.just("hello")
.doOnNext(v -> System.out.println("just " + Thread.currentThread().getName()))
.publishOn(Scheduler.boundedElastic())
.doOnNext(v -> System.out.println("publish " + Thread.currentThread().getName()))
.delayElements(Duration.ofMillis(500))
.subscribeOn(Schedulers.elastic())
.subscribe(v -> System.out.println(v + " delayed " + Thread.currentThread().getName()));
I am just learning Rx-java and Rxandroid2 and I am just confused what is the major difference between in SubscribeOn and ObserveOn.
SubscribeOn specify the Scheduler on which an Observable will operate.
ObserveOn specify the Scheduler on which an observer will observe this Observable.
So basically SubscribeOn is mostly subscribed (executed) on a background thread ( you do not want to block the UI thread while waiting for the observable) and also in ObserveOn you want to observe the result on a main thread...
If you are familiar with AsyncTask then SubscribeOn is similar to doInBackground method and ObserveOn to onPostExecute...
In case you find the above answer full of jargons:
tl;dr
Observable.just("Some string")
.map(str -> str.length())
.observeOn(Schedulers.computation())
.map(length -> 2 * length)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(---)
Observe an observable... perform the map function in an IO thread (since we are "subscribingOn" that thread)... now switch to a Computation Thread and perform map(length -> 2 * length) function... and finally make sure you Observe the output on (observeOn()) Main thread.
Anyway,
observeOn() simply changes the thread of all operators further Downstream. People usually have this misconception that observeOn also acts as upstream, but it doesn't.
The below example will explain it better...
Observable.just("Some string") // UI
.map(str -> str.length()) // UI
.observeOn(Schedulers.computation()) // Changing the thread
.map(length -> 2 * length) // Computation
.subscribe(---) // Computation
subscribeOn() only influences the thread which is going to be used when Observable is going to get subscribed to and it will stay on it downstream.
Observable.just("Some String") // Computation
.map(str -> str.length()) // Computation
.map(length -> 2 * length) // Computation
.subscribeOn(Schedulers.computation()) // -- changing the thread
.subscribe(number -> Log.d("", "Number " + number)); // Computation
Position does not matter (subscribeOn())
Why?
Because it affects only the time of subscription.
Methods that obey the contact with subscribeOn
-> Basic example : Observable.create
All the work specified inside the create body will run on the thread specified in subscribeOn.
Another example: Observable.just,Observable.from or Observable.range
Note: All those methods accept values, so do not use blocking methods to create those values, as subscribeOn won't affect it.
If you want to use blocking functions, use
Observable.defer(() -> Obervable.just(blockingMenthod())));
Important Fact:
subscribeOn does not work with Subjects
Multiple subscribeOn:
If there are multiple instances of subscribeOn in the stream, only the first one has a practical effect.
Subscribe & subscribeOn
People think that subscribeOn has something to do with Observable.subscribe, but it doesn't have anything special to do with it.
It only affects the subscription phase.
Source : Tomek Polański (Medium)
Summary
Use observeOn to set threads for callbacks "further down the stream (below it)", such as code blocks inside doOnNext or map.
Use subscribeOn to set threads for initializations "upstream (above it)", such as doOnSubscribe, Observable.just or Observable.create.
Both methods can be called multiple times, with each call overwriting previous ones. Position matters.
Let's walk through this topic with an example: we want to find the length of the string "user1032613". This is not an easy task for computers, so it's only natural that we perform the intense calculation in a background thread, to avoid freezing the app.
observeOn
We can call observeOn as many times as we like, and it controls which thread all callbacks below it will run. It's easy to use, and works just as you'd expect.
For example, we will show a progress bar on the main UI thread, then do intensive/blocking operations in another thread, then come back to the main UI thread to update the result:
Observable.just("user1032613")
.observeOn(mainThread) // set thread for operation 1
.doOnNext {
/* operation 1 */
print("display progress bar")
progressBar.visibility = View.VISIBLE
}
.observeOn(backThread) // set thread for operation 2 and 3
.map {
/* operation 2 */
print("calculating")
Thread.sleep(5000)
it.length
}
.doOnNext {
/* operation 3 */
print("finished calculating")
}
.observeOn(mainThread) // set thread for operation 4
.doOnNext {
/* operation 4 */
print("hide progress bar and display result")
progressBar.visibility = View.GONE
resultTextView.text = "There're $it characters!"
}
.subscribe()
In the above example, /* operation 1 */ is ran in the mainThread because we set it using observeOn(mainThread) on the line right above it; then we switch to backThread by calling observeOn again, so /* operation 2 */ will run there. Because we didn't change it before chaining /* operation 3 */, it will run in the back thread as well, just like /* operation 2 */; finally we call observeOn(mainThread) again, to make sure /* operation 4 */ updates the UI from the main thread.
subscribeOn
So we've learned observeOn sets threads for subsequent callbacks. What else are we missing? Well, the Observable itself, and its methods such as just(), create(), subscribe() and so on, are also code that needs to be executed. This is how objects are passed along the stream. We use subscribeOn to set threads for code related to Observable itself.
If we remove all the callbacks (controlled by observeOn discussed earlier), we are left with the "skeleton code" that will, by default, run on whichever thread the code is written in (probably main thread):
Observable.just("user1032613")
.observeOn(mainThread)
.doOnNext {
}
.observeOn(backThread)
.map {
}
.doOnNext {
}
.observeOn(mainThread)
.doOnNext {
}
.subscribe()
If we aren't happy about this empty skeleton code running on main thread, we can use subscribeOn to change it. For example, maybe the first line Observable.just("user1032613") isn't as simple as creating a stream from my user name - maybe it's a string from the Internet, or perhaps you are using doOnSubscribe for some other intensive operations. In that case, you can call subscribeOn(backThread) to put some of the code in another thread.
Where to put subscribeOn
At the time of writing this answer, there are some misconceptions saying "only call it once", "position does not matter", and "if you call it multiple times, only the first time counts". After lots of researches and experiments, it turns out subscribeOn can be usefully called multiple times.
Because Observable uses Builder Pattern (fancy name for "chaining methods one after another"), subscribeOn is applied in reverse order. Therefore, this method sets the thread for code above it, exactly the opposite of observeOn.
We can experiment this using doOnSubscribe method. This method is triggered on the subscription event, and it runs on the thread set by subscribeOn:
Observable.just("user1032613")
.doOnSubscribe {
print("#3 running on main thread")
}
.subscribeOn(mainThread) // set thread for #3 and just()
.doOnNext {
}
.map {
}
.doOnSubscribe {
print("#2 running on back thread")
}
.doOnNext {
}
.subscribeOn(backThread) // set thread for #2 above
.doOnNext {
}
.doOnSubscribe {
print("#1 running on default thread")
}
.subscribe()
It might be easier to follow the logic, if you read the above example from bottom to top, just like how Builder Pattern executes the code.
In this example, the first line Observable.just("user1032613") is run in the same thread as print("#3") because there are no more subscribeOn in-between them. This creates the illusion of "only the first call matters" for people who only care about code inside just() or create(). This quickly falls apart once you start doing more.
Footnote:
Threads and print() functions in the examples are defined, for brevity, as follows:
val mainThread = AndroidSchedulers.mainThread()
val backThread = Schedulers.computation()
private fun print(msg: String) = Log.i("", "${Thread.currentThread().name}: $msg")
If someone finds rx java description hard to understand (as me for example), here is pure java explanation:
subscribeOn()
Observable.just("something")
.subscribeOn(Schedulers.newThread())
.subscribe(...);
Is equivalent of:
Observable observable = Observable.just("something");
new Thread(() -> observable.subscribe(...)).start();
Because Observable emits values on subscribe() and here subscribe() goes in the separate thread, the values are also emitted in the same thread as subscribe(). This is why it works "upstream" (influences the thread for the previous operations) and "downstream".
observeOn()
Observable.just("something")
.observeOn(Schedulers.newThread())
.subscribe(...);
Is equivalent of:
Observable observable = Observable.just("something")
.subscribe(it -> new Thread(() -> ...).start());
Here Observable emits values in the main thread, only the listener method is executed in the separate thread.
When you subscribe to an observable, a flow starts that works its way up to the top of chain and then back down again. The subscribe part is relevant to the upward chaining and the observe part is relevant to the downward chaining.
Once the top of the chain is reached, the subscription phase has essentially completed. Events start to be emitted and the downward chain of maps, filters etc are invoked.
SubscribeOn influences subscription calls above its placement, for example doOnSubscribe.
ObserveOn influences observation calls below its placement, for example, doOnNext, map, flatmap etc.
Both will change the thread that is used to continue the flow either upward or downward.
import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers;
import java.util.concurrent.CountDownLatch;
public class SubscribeVsObserveOn {
public static void main(String[] args) throws InterruptedException {
System.out.println("Ordinal 0: " + Thread.currentThread().getName());
final CountDownLatch latch = new CountDownLatch(1);
Observable
.just("a regular string.")
.doOnSubscribe(disposable ->
System.out.println("Ordinal 2: " + Thread.currentThread().getName()))
.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.newThread())
.doOnNext(s ->
System.out.println("Ordinal 3: " + Thread.currentThread().getName()))
.map(s -> s)
.doOnSubscribe(disposable ->
System.out.println("Ordinal 1: " + Thread.currentThread().getName()))
.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.newThread())
.doOnNext(s ->
System.out.println("Ordinal 4: " + Thread.currentThread().getName()))
.map(s -> s)
.subscribe(s -> latch.countDown());
latch.await();
}
}
Here is the output:
Ordinal 0: main
Ordinal 1: RxNewThreadScheduler-1
Ordinal 2: RxNewThreadScheduler-2
Ordinal 3: RxNewThreadScheduler-3
Ordinal 4: RxNewThreadScheduler-4
This answer is nothing new, I just want to clarify a little bit more.
Let's assume that we have two threads.
val pool1 = Executors.newCachedThreadPool { runnable -> Thread(runnable, "Thread 1") }
val pool2 = Executors.newCachedThreadPool { runnable -> Thread(runnable, "Thread 2") }
As the answers described, observeOn will set Downstream, and subscribeOn will set Upstream. But what if both of them was used? For check this, I added logs line by line.
Observable.just("what if use both")
.doOnSubscribe { Log.d("Thread", "both, doOnSubscribe A " + Thread.currentThread().name) }
.doOnNext { Log.d("Thread", "both, doOnNext A " + Thread.currentThread().name) }
.map {
Log.d("Thread", "both, map A " + Thread.currentThread().name)
it + " A"
}
// observeOn
.observeOn(Schedulers.from(pool1))
.doOnSubscribe { Log.d("Thread", "both, doOnSubscribe B " + Thread.currentThread().name) }
.doOnNext { Log.d("Thread", "both, doOnNext B " + Thread.currentThread().name) }
.map {
Log.d("Thread", "both, map B " + Thread.currentThread().name)
it + " B"
}
// subscribeOn
.subscribeOn(Schedulers.from(pool2))
.doOnSubscribe { Log.d("Thread", "both, doOnSubscribe C " + Thread.currentThread().name) }
.doOnNext { Log.d("Thread", "both, doOnNext C " + Thread.currentThread().name) }
.map {
Log.d("Thread", "both, map C " + Thread.currentThread().name)
it + " C"
}
// observeOn main
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { Log.d("Thread", "main " + Thread.currentThread().name) }
.subscribe(
{ result -> Log.d("Thread", "main subscribe " + Thread.currentThread().name)}
, { error -> {} }
)
The result is:
both, doOnSubscribe C main
both, doOnSubscribe A Thread 2
both, doOnSubscribe B Thread 2
both, doOnNext A Thread 2
both, map A Thread 2
both, doOnNext B Thread 1
both, map B Thread 1
both, doOnNext C Thread 1
both, map C Thread 1
main main
main subscribe main
result: what if use both A B C
As you can see, doOnSubscribe called first, from bottom to top. That means subscribe has priority over other operators, so the first thread which handles first code was Thread 2.
And then other operators was called, line by line. After observeOn, thread was changed to Thread 1. Then, just before calling subscribe, observeOn was called again, for change thread to main thread. (Don't care about AndroidSchedulers, it is just a kind of scheduler)
TL;DR;
First path, subscribeOn called first, from bottom to top.
Second path, observeOn called, from top to bottom, along with other codes.
Behavior was same on both RxJava2 and RxJava3
I have written up a contrived code example, and it might not be code that someone ought to use, but I believe it should work. However it instead deadlocks. I've read the answers described here, but found them insufficient.
Here is the code example:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class Test {
public static void main(String argv[]) throws Exception {
int nThreads = 1;
Executor executor = Executors.newFixedThreadPool( nThreads );
CompletableFuture.completedFuture(true)
.thenComposeAsync((unused)->{
System.err.println("About to enqueue task");
CompletableFuture<Boolean> innerFuture = new CompletableFuture<>();
executor.execute(() -> {
// pretend this is some really expensive computation done asynchronously
System.err.println("Inner task");
innerFuture.complete(true);
});
System.err.println("Task enqueued");
return innerFuture;
}, executor).get();
System.err.println("All done");
System.exit(0);
}
}
This prints:
About to enqueue task
Task enqueued
And then it hangs. It's deadlocked because the executor only has a single thread, and it's waiting for the innerFuture to become redeemable. Why does "thenComposeAsync" block for its return value to become redeemable, instead of returning the still-incomplete future and freeing up its thread in the executor?
This feels completely unintuitive, and the javadocs don't really help. Am I fundamentally misunderstanding how CompletionStages work? Or is this a bug in the implementation?
So, after a lot of interesting conversation, I decided to email one of the JDK authors. Found out that this behavior wasn't intended, and is indeed a bug present in 1.8u25. There is a fix that will be released with a later patch version of Java 8. I don't know which. For anyone wanting to test the new behavior, you can download the latest jsr166 jar here:
http://gee.cs.oswego.edu/dl/concurrency-interest/index.html
First, let me rewrite your code with 2 static functions to make it easier to see what's going on:
// Make an executor equivalent to Executors.newFixedThreadPool(nThreads)
// that will trace to standard error when a task begins or ends
static ExecutorService loggingExecutor(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>()) {
#Override
protected void beforeExecute(Thread t, Runnable r) {
System.err.println("Executor beginning task on thread: "
+ t.getName());
}
#Override
protected void afterExecute(Runnable r, Throwable t) {
System.err.println("Executor finishing task on thread: "
+ Thread.currentThread().getName());
}
};
}
And
// same as what you pass to thenComposeAsync
static Function<Boolean, CompletableFuture<Boolean>> inner(Executor executor) {
return b -> {
System.err.println(Thread.currentThread().getName()
+ ": About to enqueue task");
CompletableFuture<Boolean> innerFuture = new CompletableFuture<>();
executor.execute(() -> {
System.err.println(Thread.currentThread().getName()
+ ": Inner task");
innerFuture.complete(true);
});
System.err.println(Thread.currentThread().getName()
+ ": Task enqueued");
return innerFuture;
};
}
Now we can write your test case as follows:
ExecutorService e = loggingExecutor(1);
CompletableFuture.completedFuture(true)
.thenComposeAsync(inner(e), e)
.join();
e.shutdown();
/* Output before deadlock:
Executor beginning task on thread: pool-1-thread-1
pool-1-thread-1: About to enqueue task
pool-1-thread-1: Task enqueued
*/
Let's test your conclusion that the first thread is not released until the result of the second future is computed:
ExecutorService e = loggingExecutor(2); // use 2 threads this time
CompletableFuture.completedFuture(true)
.thenComposeAsync(inner(e), e)
.join();
e.shutdown();
/*
Executor beginning task on thread: pool-1-thread-1
pool-1-thread-1: About to enqueue task
pool-1-thread-1: Task enqueued
Executor beginning task on thread: pool-1-thread-2
pool-1-thread-2: Inner task
Executor finishing task on thread: pool-1-thread-2
Executor finishing task on thread: pool-1-thread-1
*/
Indeed, it appears that thread 1 is held until thread 2 is done
Let's see if you are right that thenComposeAsync itself blocks:
ExecutorService e = loggingExecutor(1);
CompletableFuture<Boolean> future =
CompletableFuture.completedFuture(true)
.thenComposeAsync(inner(e), e);
System.err.println("thenComposeAsync returned");
future.join();
e.shutdown();
/*
thenComposeAsync returned
Executor beginning task on thread: pool-1-thread-1
pool-1-thread-1: About to enqueue task
pool-1-thread-1: Task enqueued
*/
thenComposeAsync didn't block. It returned the CompletableFuture right away and the deadlock only occurred when we tried to complete it. So what should it take to complete the future returned by .thenComposeAsync(inner(e), e)?
The API needs to wait for innner(e) to return CompletableFuture<Boolean>
it needs to wait for the returned CompletableFuture<Boolean> to also complete. Only then is the future complete. So, as you can see, it cannot do what you suggest and return the incomplete Future.
Is it a bug? Why does the CompletionStage hold on to thread 1 while the inner task is being computed? It is not a bug becuase, as you noted, the documentation is pretty vague and doesn't promise to release threads in any particular order. Also, note that Thread1 will be used for any subsequent then*() methods of CompletableFuture. Consider the following:
ExecutorService e = loggingExecutor(2);
CompletableFuture.completedFuture(true)
.thenComposeAsync(inner(e), e)
.thenRun(() -> System.err.println(Thread.currentThread().getName()
+ ": All done"))
.join();
e.shutdown();
/*
Executor beginning task on thread: pool-1-thread-1
pool-1-thread-1: About to enqueue task
pool-1-thread-1: Task enqueued
Executor beginning task on thread: pool-1-thread-2
pool-1-thread-2: Inner task
Executor finishing task on thread: pool-1-thread-2
pool-1-thread-1: All done
Executor finishing task on thread: pool-1-thread-1
*/
As you can see, .thenRun(...) got executed on thread 1. I believe this is consistent with other *Async(... , Executor exec) methods of CompletableFuture.
But what if you want to split up the functionality of thenComposeAsync into 2 separately controllable steps instead of leaving it up to the API to juggle threads? You can just do this:
ExecutorService e = loggingExecutor(1);
completedFuture(true)
.thenApplyAsync(inner(e), e) // do the async part first
.thenCompose(x -> x) // compose separately
.thenRun(() -> System.err.println(Thread.currentThread().getName()
+ ": All done"))
.join();
e.shutdown();
Everything will run nicely on 1 thread with no deadlocks.
In conclusion, is this behavior of unintuitive as you say? I don't know. I cannot imagine why thenComposeAsync even exists. If a method returns CompletableFuture, it shouldn't block and there should be no reason to call it asynchronously.
I have the following code:
public void run()
{
try
{
logger.info("Looking for new tasks to fetch... ");
// definitions ..
for(Task t: tasks)
{
logger.info(" Task " + t.getId() + " is being fetched ");
// processing ... fetching task info from db using some methods
}
Thread.sleep(FREQUENCY);
//t.start();
} catch (Exception e)
{
logger.info("FetcherThread interrupted: "+e.getMessage());
}
}
I'm trying to make the thread to sleep for a specific time "FREQUENCY" and then work again. when I execute this code in eclipse, the thread works only once and then nothing happens and process terminates. If I remove the comment from the statement: t.start(), I get "FetcherThread interrupted: null".
Can anyone tell me where I'm going wrong?
N.B.: I want the thread to be working all the time, but fetching on periods (say every 5 minutes)
You're missing any sort of loop in that code.
It seems that the thread is actually doing what you tell it to do: it runs all the tasks, then sleeps for a bit - then it has no more work to do, and so exits. There are several ways to address this, in ascending order of complexity and correctness:
The simple (and naive) way to address this is to wrap the try-catch block in an infinite loop (while(true) { ... }). This way after the thread finishes sleeping, it will loop back to the top and process all the tasks again.
However this isn't ideal, as it's basically impossible to stop the thread. A better approach is to declare a boolean field (e.g. boolean running = true;), and change the loop to while(running). This way, you have a way to make the thread terminate (e.g. expose a method that sets running to false.) See Sun's Why is Thread.stop() deprecated article for a longer explanation of this.
And taking a step further back, you may be trying to do this at too low a level. Sleeping and scheduling isn't really part of the job of your Runnable. The actual solution I would adopt is to strip out the sleeping, so that you have a Runnable implementation that processes all the tasks and then terminates. Then I would create a ScheduledExecutorService, and submit the "vanilla" runnable to the executor - this way it's the job of the executor to run the task periodically.
The last solution is ideal from an engineering perspective. You have a class that simply runs the job once and exits - this can be used in other contexts whenever you want to run the job, and composes very well. You have an executor service whose job is the scheduling of arbitrary tasks - again, you can pass different types of Runnable or Callable to this in future, and it will do the scheduling bit just as well. And possibly the best part of all, is that you don't have to write any of the scheduling stuff yourself, but can use a class in the standard library which specifically does this all for you (and hence is likely to have the majority of bugs already ironed out, unlike home-grown concurrency code).
Task scheduling has first-class support in Java, don't reinvent it. In fact, there are two implementations: Timer (old-school) and ScheduledExecutorService (new). Read up on them and design your app aroud them.
Try executing the task on a different thread.
You need some kind of loop to repeat your workflow. How shall the control flow get back to the fetching part?
You can put the code inside a loop.( May be while)
while(condition) // you can make it while(true) if you want it to run infinitely.
{
for(Task t: tasks)
{
logger.info(" Task " + t.getId() + " is being fetched ");
// processing ... fetching task info from db using some methods
}
Thread.sleep(FREQUENCY);
}
Whats happening in your case its running the Task loop then sleeping for some time and exiting the thread.
Put the thread in a loop as others have mentioned here.
I would like to add that calling Thread.start more than once is illegal and that is why you get an exception.
If you would like to spawn multiple thread create one Thread object per thread you want to start.
See http://docs.oracle.com/javase/6/docs/api/java/lang/Thread.html#start()
public void run()
{
while (keepRunning) {
try
{
logger.info("Looking for new tasks to fetch... ");
// definitions ..
for(Task t: tasks)
{
logger.info(" Task " + t.getId() + " is being fetched ");
// processing ... fetching task info from db using some methods
t.start();
}
Thread.sleep(FREQUENCY);
} catch (Exception e) {
keepRunning = false;
logger.info("FetcherThread interrupted: "+e.getMessage());
}
}
}
Add a member call keepRunning to your main thread and implement an accessor method for setting it to false (from wherever you need to stop the thread from executing the tasks)
You need to put the sleep in an infinite loop (or withing some condition specifying uptill when you want to sleep). As of now the sleep method is invoked at the end of the run method and behavior you observe is correct.
The following demo code will print "Sleep" on the console after sleeping for a second. Hope it helps.
import java.util.concurrent.TimeUnit;
public class Test implements Runnable {
/**
* #param args
*/
public static void main(String[] args) {
Test t = new Test();
Thread thread = new Thread(t);
thread.start();
}
public void run() {
try {
// logger.info("Looking for new tasks to fetch... ");
// definitions ..
// for(Task t: tasks)
// {
// logger.info(" Task " + t.getId() + " is being fetched ");
// // processing ... fetching task info from db using some methods
// }
while (true) { // your condition here
TimeUnit.SECONDS.sleep(1);
System.out.println("Sleep");
}
// t.start();
} catch (Exception e) {
// logger.info("FetcherThread interrupted: "+e.getMessage());
}
}
}
You could try ScheduledExecutorService (Javadoc).
And us it's scheduleAtFixedRate, which:
Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given period; that is executions will commence after initialDelay then initialDelay+period, then initialDelay + 2 * period, and so on.