I want to work with two threads in my Java program for a tiny part. I need to give the first call to a database and the second call to an API, both calls with same input, and then work with the output of whichever thread finishes first.
It's my first time programming with threads and I'm very confused. I've seen tutorials and they mainly explain how to get two separate things done with threads so I'm a little lost.
Can someone please help or re-direct me to any useful link they may have?
So far, as I understand it, should it look something like this? :
Thread thread1 = new Thread(func1());
Thread thread2 = new Thread(func2());
thread1.start();
thread2.start();
But then how do I extract the output of the functions? How would I know which one has finished first?
-----------UPDATE 1---------
After trying CompletableFuture (thanks for the help Johan!) I have something like this:
CompletableFuture<Object> getData = CompletableFuture.anyOf(
CompletableFuture.runAsync(() -> getDataFromDB(clientData)),
CompletableFuture.runAsync(() -> getDataFromApi(clientData))
);
getData.thenApply(dataObject -> {
// Cast the returned Object to the actual type of your data,
// assuming both getDataFromDb and getDataFromApi
// return the same result type
Object data = (String) dataObject;
// Work with the returned data.
result = (String) data;
});
But I get this error for getData.thenApply():
The method thenApply(Function) in the type CompletableFuture is not applicable for the arguments (( dataObject) -> {})
Since I know that getData in of type String, would it be okay to just convert it to String and store the result?
As #Johan Hirsch suggests try with CompletableFuture. I've just try this and it works:
CompletableFuture.anyOf(
CompletableFuture.supplyAsync(() -> getDataFromDB(clientData)),
CompletableFuture.supplyAsync(() -> getDataFromApi(clientData)))
.thenApply(item -> (String) item)
.thenAccept(result -> {
// Consume the data
System.out.println(result);
});
Beware that I'm currently consuming the data so it doesn't return anything. If you just want to pass the result to another CompletableFuture change the thenAccept method for a thenApply
Java 8 provides a very nice utility class called CompletableFuture, which can help in your case.
Create two CompletableFuture, one for each of your tasks, and then use the CompletableFuture.anyOf method to wait for either one to finish.
CompletableFuture<TData> getData = CompletableFuture.anyOf(
CompletableFuture.runAsync(() -> getDataFromDb()),
CompletableFuture.runAsync(() -> getDataFromApi())
);
getData.thenApply(dataObject -> {
// Cast the returned Object to the actual type of your data,
// assuming both getDataFromDb and getDataFromApi
// return the same result type
TData data = (TData)dataObject;
// Work with the returned data.
processData(data);
});
You can use ExecutorService.invokeAny
Executes the given tasks, returning the result of one that has completed successfully (i.e., without throwing an exception), if any do. Upon normal or exceptional return, tasks that have not completed are cancelled. The results of this method are undefined if the given collection is modified while this operation is in progress.
Related
I have a workflow like: startevent -> task1(Assignee:Tom) -> choose sequence flow "agree" ->task2(Assignee:Jerry) -> choose sequence flow "disagree" -> task1
When the flow arrive to task1, i want to set assignee to "Tom" again.
Now i have an idea like:
When the flow arrive to task1, i use complete method, after the complete method, set a local variable "pre_task_id(task1's taskid)" in task2 so that i can use task1's taskid to search in "act_hi_taskinst" table for assignee(Tom), but this method taskService.setVariableLocal(taskId, variableName, value) need task2's taskid, how can i get the task2's taskid after complete method?
#Test
public void testCompleteTask() {
Task task = taskService.createTaskQuery().taskAssignee("Tom").singleResult();
if (task == null) {
System.out.println("no task!!!");
return;
}
String preTaskId = task.getId();
HashMap <String,Object> variables = new HashMap<>();
variables.put("userId", "Jerry");
variables.put("oper", "saolu");
taskService.complete(task.getId(),variables);
//don't konw how to get the taskId
//taskService.setVariableLocal(taskId, "pre_task_id", preTaskId);
}
I am using activiti6
Or please let me konw if there are any better solutions
workflow.png
If I understand properly. You need to keep track which task is executed most recently.
In that case you can use a stack. When ever you execute any task you just call the push with the task id.
When ever you call pop it will give you the task id of the most recent task executed.
Just be careful about how you want to clear the stack as well.
I would recommend to always call pop before calling push.
In the past I have always used a process variable for this purpose, Have never attempted the "push" operation suggested above but I would be concerned about getting the correct value back if multiple parallel processes are all pushing/pulling to and from the stack at once. The process variable is simple and I know it works.
I'm working on a project with a lot of CompletableFuture.completedFuture ... thenAccept codes, e.g.
public CompletableFuture<Boolean> callee() {
boolean result = ... // Do something and get result - Step A
return CompletableFuture.completedFuture(Boolean.valueOf(result));
}
public void caller() {
callee().thenAccept(result -> {
// Detect if call success or failure - Step B
new Throwable().printStackTrace(); // the debug code: stacktrace shows it is called from caller
});
}
I concluded that Step A and Step B are called sequentially in one thread.
So can I simplify it like this?
public boolean callee() {
boolean result = ... // Do something and get result
return result;
}
public void caller() {
boolean result = callee();
// Detect if call success or failure
}
Yes, you can simplify it like this. The long version:
I think the question should be rather: "Is this usage of CompletableFuture appropriate?". No, it's not. This code is using CompletableFuture like a wrapper, a package, to pass data around and not as a tool to execute code asynchronously. This tool can be used to pass data around between threads, but it's not what this code is doing.
Calling CompletableFuture.completedFuture does nothing but create a new CompletableFuture that is completed with whatever you pass to the method. Then you call thenAccept on it, which has basically the following effect: "Take the result when it's done and let the thread that has calculated the result execute the following code. If the result is already calculated, let the caller execute the following code themself." The "following code" is simply the lambda you pass to thenAccept.
The initial CompletableFuture is completed instantly and the following code gets executed by the thread that calls thenAccept directly. The thread that executes caller and callee does everything itself. So this part is effectively doing nothing asynchronously. Therefore, the code is equivalent to the simpler code in the second example without CompletableFuture.
To actually make use of CompletableFuture, you should run boolean result = ... // Do something and get result - Step A asynchronously by e.g. creating this initial future using CompletableFuture.supplyAsync. The chained code will also be run asynchronously.
I have an issue while processing a flux that is built from a Stream.generate construct.
The Java stream is fetching some data from a remote source, hence I implemented a custom supplier that has the data fetching logic embedded, and then used it to populate the Stream.
Stream.generate(new SearchSupplier(...))
My idea is to detect an empty list and use the Java9 feature of takeWhile ->
Stream.generate(new SearchSupplier(this, queryBody))
.takeWhile(either -> either.isRight() && either.get().nonEmpty())
(using Vavr's Either construct)
The repositoroy layer flux will then do:
return Flux.fromStream (
this.searchStream(...) //this is where the stream gets generated
)
.map(Either::get)
.flatMap(Flux::fromIterable);
The "service" layer is composed of some transformation steps on the flux, but the method signature is something like Flux<JsonObject> search(...).
Finally, the controller layer has a GetMapping:
#GetMapping(produces = "application/stream+json")
public Flux search(...) {
return searchService.search(...) //this is the Flux<JsonObject> parth
.subscriberContext(...) //stuff I need available during processing
.doOnComplete(() -> log.debug("DONE"));
}
My problem is that the Flux seems to never terminate.
Doing a call from Postman for example just shot the 'Loading...' part in the response section. When I terminate the process from my IDE the results are then flushed to postman and I see what I'm expecting. Also the doOnComplete lambda never gets called
What I noticed is that if I change the source of a Flux:
Flux.fromArray(...) //harcoded array of lists of jsons
the doOnComplete lambda is called and also the http connection closes, and results are displayed in postman.
Any idea of what might be the issue?
Thanks.
You could create the Flux directly using code that looks like this. Note that I'm adding some assumed methods which you would need to implement based on your how your SearchSupplier works:
Flux<SearchResultType> flux = Flux.generate(
() -> new SearchSupplier(this, queryBody),
(supplier, sink) -> {
SearchResultType current = supplier.next();
if (isNotLast(current)) {
sink.next(current);
} else {
sink.complete();
}
return supplier;
},
supplier -> anyCleanupOperations(supplier)
);
I am trying to convert Flowable<List<TaskEntity>> to Flowable<List<Task>> but something is wrong.
To understand the problem I tried with converting a simpler list and it is working fine. When I try to apply the same logic to my actual problem, it is not working.
This logic is giving me expected output. [No.1 No.2 No.3]
Flowable.fromArray(Arrays.asList(1,2,3))
.flatMapIterable(ids->ids)
.map(s->"No. "+s)
.toList()
.toFlowable()
.subscribe(
t -> Log.d(TAG, "getAllActiveTasks: "+t)
);
This logic is not working . It prints Nothing
mTaskDao.getAllTasks(STATE_ACTIVE)
.flatMapIterable(task -> task)
.map(Task::create)
.toList()
.toFlowable()
.subscribe(
t -> Log.d(TAG, "getAllActiveTasks: "+t)
);
Edit 1
This is how Task.create() looks like.
public static Task create(TaskEntity eTask) {
Task task = new Task(eTask.getTaskId(), eTask.getTaskTitle(), eTask.getTaskStatus());
task.mTaskDescription = eTask.getTaskDescription();
task.mCreatedAt = eTask.getCreatedAt();
task.mTaskDeadline = eTask.getTaskDeadline();
return task;
}
Solution
As mentioned in the comments, toList() can only work if emitting source has finite number of items to emit. Since Flowable from Dao method contains an infinite stream of objects, toList() was not being used correctly by me.
Checkout this comment for the exact way to solve this problem.
https://stackoverflow.com/a/50318832/4989435
toList requires a finite source but getAllTasks is likely infinite, which is unfortunately quite typical from DAOs backed by Android databases. Change the getAllTasks to Single, use take(1), use timeout(), or use flatMap(Observable.fromIterable().map().toList()) instead of flatMapIterable.
I want to receive any updates made to tasks in db.
In this case, you need the latter option:
mTaskDao.getAllTasks(STATE_ACTIVE)
.flatMapSingle(task ->
Observable.fromIterable(task)
.map(Task::create)
.toList()
)
.subscribe(
t -> Log.d(TAG, "getAllActiveTasks: "+t)
);
You should use only map operator to convert TaskEntity to Task. I have created sample. You can check my solution which uses only map operator
Since I´m using Vertx 3.1 in my stack, I was thinking to use the Future feature that the tools brings, but after read the API seems pretty limited to me. I cannot even find the way to make the the future wait for an Observable.
Here my code
public Observable<CommitToOrderCommand> validateProductRestrictions(CommitToOrderCommand cmd) {
Future<Observable<CommitToOrderCommand>> future = Future.future();
orderRepository.getOrder(cmd, cmd.orderId)
.flatMap(order -> validateOrderProducts(cmd, order))
.subscribe(map -> checkMapValues(map, future, cmd));
Observable<CommitToOrderCommand> result = future.result();
if(errorFound){
throw MAX_QUANTITY_PRODUCT_EXCEED.create("Fail"/*restrictions.getBulkBuyLimit().getDescription())*/);
}
return result;
}
private void checkMapValues(Multimap<String, BigDecimal> totalUnitByRestrictions, Future<Observable<CommitToOrderCommand>> future,
CommitToOrderCommand cmd) {
for (String restrictionName : totalUnitByRestrictions.keySet()) {
Restrictions restrictions = Restrictions.valueOf(restrictionName);
if (totalUnitByRestrictions.get(restrictionName)
.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add)
.compareTo(restrictions.getBulkBuyLimit()
.getMaxQuantity()) == 1) {
errorFound = true;
}
}
future.complete(Observable.just(cmd));
}
In the onComplete of my first Observable I´m checking the results, and after finish is when I finish the future to unblock the operation.
But I´m looking that future.result is not block until future.complete is invoke as I was expecting. Instead is just returning null.
Any idea what´s wrong here?
Regards.
The vertx future doesn't block but rather work with a handler that is invoked when a result has been injected (see setHandler and isComplete).
If the outer layer of code requires an Observable, you don't need to wrap it in a Future, just return Observable<T>. Future<Observable<T>> doesn't make much sense, you're mixing two ways of doing async results.
Note that there are ways to collapse an Observable into a Future, but the difficulty is that an Observable may emit several items whereas a Future can hold only a single item. You already took care of that by collecting your results into a single emission of map.
Since this Observable only ever emits one item, if you want a Future out of it you should subscribe to it and call future.complete(yourMap) in the onNext method. Also define a onError handler that will call future.fail.