[concurrency-interest] CompletableStage.whenComplete(): Completes the stage after calling the BiConsumer

Nathan and Ila Reynolds nathanila at gmail.com
Thu Nov 7 20:25:06 EST 2019


Ah!  Thank you!  That makes working with CompletableFutures much easier!  I
can attach all sorts of things without having to worry about keeping a
reference to the returned CompletableFuture.

On Thu, Nov 7, 2019 at 5:55 PM Alex Otenko <oleksandr.otenko at gmail.com>
wrote:

> The existence of happens before means you write BiConsumer like
> single-threaded.
>
> The BiConsumer is attached to this, and a new CompletionStage is retained
> until BiConsumer completes, at which stage it is completed. If you drop the
> reference to the stage returned by whenComplete, you just won't know when
> it completes, and how. But you can't stop its execution by dropping the
> reference.
>
> Alex
>
> On Fri, 8 Nov 2019, 00:39 Nathan and Ila Reynolds, <nathanila at gmail.com>
> wrote:
>
>> Hmm... I just now recognize that there is a happens before edge for
>> whenComplete()'s action returns and when "future" completes.  How does
>> having that happens-before edge help write code?  I suppose it allows me to
>> carry out actions that must be visible before "future" completes and
>> actions that must execute after "future" completes.  Are there other
>> abstract or concrete reasons?
>>
>> > Otherwise the simplified example doesn't make it clear why you do not
>> assign async to future.
>>
>> I could store "async" in a separate field but that wastes heap space when
>> I figure I only need 1 field.
>>
>> I could change startSomething() to the following.  I assume that "unused"
>> could be GCed and whenComplete() will never execute.  Is this assumption
>> correct?
>>
>> public void startSomething()
>> {
>>    CompletableFuture<String> unused;
>>
>>    future = CompletableFuture.supplyAsync(() -> produce());
>>    unused = future.whenComplete((value, throwable) -> doSomething1());
>> }
>>
>> -Nathan
>>
>> On 11/7/2019 5:30 PM, Alex Otenko wrote:
>>
>> UnableToInferImplicitsException
>>
>> You need to start with happens before edges. Some of them are program
>> order.
>>
>> What do you want to happen before future can be seen completed, and what
>> do you want to happen after it? Sounds tautological, but that's where you
>> need to split the execution into two stages.
>>
>> Otherwise the simplified example doesn't make it clear why you do not
>> assign async to future.
>>
>> Alex
>>
>> On Thu, 7 Nov 2019, 23:44 Nathan and Ila Reynolds via
>> Concurrency-interest, <concurrency-interest at cs.oswego.edu> wrote:
>>
>>> UnableToParseQuestionException  ;)
>>>
>>> Let's use the following simplified code.
>>> CompletableFuture<String> future;
>>>
>>> public void startSomething()
>>> {
>>>    CompletableFuture<String> async;
>>>
>>>    async  = CompletableFuture.supplyAsync(() -> produce());
>>>    future = async.whenComplete((value, throwable) -> doSomething1());
>>> }
>>>
>>> public String produce()
>>> {
>>>    Thread.sleep(1000);  // A long operation
>>>    return("Hello");
>>> }
>>>
>>> public void doSomething1()
>>> {
>>>    doSomething2();
>>> }
>>>
>>> public void doSomething2()
>>> {
>>>    doSomething3();
>>> }
>>>
>>> public void doSomething3()
>>> {
>>>    doSomething4();
>>> }
>>>
>>> public void doSomething4()
>>> {
>>>    if (future.getNow(null) == null)
>>>       throw new IllegalStateException("I wanted \"Hello\"");
>>> }
>>>
>>> Here are my questions now more refined...
>>>
>>> Why does "future" not complete until after the action in whenComplete()
>>> returns?  How do I exploit this behavior?
>>>
>>> How do I get the value from "future" in doSomething4()?
>>>
>>>
>>> I could pass the "Hello" value through the doSomething#(), but that will
>>> require creating duplicate methods because other code uses the
>>> doSomething#() as they are.  I could pass null or value depending on the
>>> use case but that makes the methods complicated.
>>>
>>> I cannot access "async" since that is long gone from the stack of
>>> probably another thread.  I could save "async" in a separate field but that
>>> wastes heap space.
>>>
>>> I could change startSomething() to the following.  If I understand
>>> correctly, "unused" could be GCed and whenComplete() will never execute.
>>>
>>> public void startSomething()
>>> {
>>>    CompletableFuture<String> unused;
>>>
>>>    future = CompletableFuture.supplyAsync(() -> produce());
>>>    unused = future.whenComplete((value, throwable) -> doSomething1());
>>> }
>>>
>>> -Nathan
>>>
>>> On 11/7/2019 2:41 PM, Martin Buchholz wrote:
>>>
>>>
>>> https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/concurrent/CompletionStage.html#whenComplete(java.util.function.BiConsumer)
>>>
>>> On Thu, Nov 7, 2019 at 10:49 AM Nathan and Ila Reynolds via
>>> Concurrency-interest <concurrency-interest at cs.oswego.edu> wrote:
>>>
>>>> For CompletableStage.whenComplete(), the Javadoc says "The returned
>>>> stage is completed when the action returns."  What is the reasoning for
>>>> completing the future after action returns?
>>>
>>>
>>> The result of the returned stage depends on whether the action failed or
>>> not.
>>>
>>>
>>>> How do I run action after
>>>> the future completes?
>>>>
>>>
>>> Why can't you just attach a completion action to either the stage
>>> returned by whenComplete or its source?
>>>
>>>
>>> _______________________________________________
>>> Concurrency-interest mailing list
>>> Concurrency-interest at cs.oswego.edu
>>> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>>>
>>

-- 
-Nathan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20191107/e7ec2706/attachment.html>


More information about the Concurrency-interest mailing list