简体   繁体   English

CompletableFuture.thenAccept中使用的垃圾回收对象的可用性

[英]Availability of objects for garbage collection used inside CompletableFuture.thenAccept

I am trying to execute an async call from a method using CompletableFuture . 我正在尝试使用CompletableFuture从方法执行异步调用。 On completion of that task, I am trying to print values of the object DummyObject which are local to the method calling Async call. 完成该任务后,我尝试打印对象DummyObject的值,这些值对于调用Async调用的方法而言是本地的。

I want to know how is it working? 我想知道它如何运作? The thread( myThread ) is dead after 3 seconds and DummyObject is out of scope, still the Async callback inside thenAccept is printing correct values. 3秒钟后,线程( myThread )死了,并且DummyObject超出了作用域,那么thenAccept中的Async回调仍在打印正确的值。 Is the thread getting a lock on DummyObject? 线程是否锁定了DummyObject? Or something else is happening? 还是正在发生其他事情?

Please note that I am just trying to simulate a scenario. 请注意,我只是在尝试模拟一个场景。 So I am using Thread.stop() . 所以我正在使用Thread.stop()

Edit - Question is about how thenAccept Consumer is handling the scope? 编辑-问题是有关thenAccept Consumer如何处理范围的? Please keep answers relevant to that. 请保持与此相关的答案。

Output of the below program: 以下程序的输出:

Starting Async stuff
Thread Alive?  false
Reached Main end and waiting for 8 more seconds
James T. Kirk
United Federation of Planets

AsyncTest.java AsyncTest.java

public class AsyncTest {

    public static void main(String args[]) {

        Thread myThread = new Thread() {
            @Override
            public void run() {
                DummyObject dummyObj = new DummyObject();
                dummyObj.setObjectName("James T. Kirk");
                dummyObj.setObjectNationality("United Federation of Planets");
                System.out.println("Starting Async stuff");
                new AsyncTaskExecuter().executeAsync().thenAccept(taskStatus -> {
                    if(taskStatus.booleanValue()) {
                        System.out.println(dummyObj.getObjectName());
                        System.out.println(dummyObj.getObjectNationality());
                    }
                });
            }
        };

        myThread.start();
        try {
            Thread.sleep(3 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myThread.stop();
        System.out.println("Thread Alive?  "+ myThread.isAlive());

        System.out.println("Reached Main end and waiting for 8 more seconds");

        try {
            Thread.sleep(8 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

class AsyncTaskExecuter {
    public CompletableFuture<Boolean> executeAsync() {
        return  (CompletableFuture<Boolean>) CompletableFuture.supplyAsync(this::theTask);
    }

    public Boolean theTask() {
        try {
            Thread.sleep(6 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return true;
    }
}

class DummyObject {
    private String objectName;
    private String objectNationality;
    // getters and setters
}

First thing. 第一件事 Please don't use stop() method. 请不要使用stop()方法。 It is deprecated. 不推荐使用。 The thread dies on its own once it finishes executing its run method. 完成执行run方法后,线程将自行死亡。

Now to answer your question, when you create a CompletableFuture, it also creates a thread in java's ForkJoinCommonPool and executes it there sometime in future. 现在回答您的问题,当您创建CompletableFuture时,它还会在Java的ForkJoinCommonPool创建一个线程,并在将来某个时间执行该线程。 So effectively in your application you are creating 3 threads. 在您的应用程序中如此有效地创建了3个线程。 So your object cannot die as it will be used by one of the thread. 因此,您的对象不会死亡,因为它将被线程之一使用。

  1. Main Thread 主线
  2. My-Thread 我的线程
  3. And final one is the one created by CompletableFuture. 最后一个是由CompletableFuture创建的。 (This will use the dummy object) (这将使用虚拟对象)

So even if thread 1, 2 die. 因此,即使线程1、2死亡。 Thread 3 still will execute. 线程3仍将执行。

在此处输入图片说明

PS I have given name to your thread as Thread-MyThread to show you the threads. PS我为您的线程命名为Thread-MyThread,以向您显示线程。 You can give name to your threads by calling Thread's constructor which takes name for the thread as parameter. 您可以通过调用线程的构造函数为线程命名,该构造函数将线程的名称作为参数。 The tool I am using is JVisualVM. 我使用的工具是JVisualVM。

At the point you are talking about, the dummyObj variable is still in scope within the lambda that you passed to thenAccept . 在您正在讨论的那一点上, dummyObj变量仍然传递给thenAccept的lambda 范围内。

In fact, the stack frame with the original variable will have gone away. 实际上,带有原始变量的堆栈框架将消失。 However, the Java compiler arranges that the object that represents the lambda has a copy of the original variable's value (ie the reference to the DummyObject instance) saved in a read-only synthetic variable. 但是,Java编译器安排代表lambda的对象具有保存在只读合成变量中的原始变量值的副本(即,对DummyObject实例的引用)。

(This is the reasoning behind the restriction that a lambda can only use a local variable in the enclosing method if the variable is effectively final . If a variable is effectively final, its value can be copied to another final variable without any risk of inconsistencies due to the variable(s) changing.) (这是限制的原因,即如果变量是有效最终变量,则lambda只能在封闭方法中使用局部变量。如果变量是有效最终变量,则可以将其值复制到另一个最终变量而不会造成任何不一致的风险更改中的变量。)

Anyhow, the lambda uses the DummyObject via the synthetic variable. 无论如何,lambda通过综合变量使用DummyObject And the DummyObject instance will remain reachable as long as the lambda is reachable. 只要lambda可达, DummyObject实例将保持可达。

If the local variable is of primitive type, what is stored on stack is its value. 如果局部变量是原始类型,则堆栈中存储的是其值。 In case of reference type local variables the reference is also stored on stack but the actual object is stored in heap. 在引用类型为局部变量的情况下,引用也存储在堆栈中,但实际对象存储在堆中。

So in case of using local variable inside an anonymous inner class(lambda in your code) the copy of what is held in stack is taken to anonymous class, in case of primitive type variable the copy is the value of variable, in case of reference type variable the copy is the reference itself. 因此,在匿名内部类(在代码中为lambda)内部使用局部变量的情况下,将堆栈中保存的内容的副本带到匿名类中,对于原始类型的变量,副本为变量的值,对于引用而言类型变量副本是引用本身。 Hence after the stack frame is gone the anonymous class either has the copy of primitive type or the copy of actual reference the value of which is stored in heap, and while there is a reference to an object garbage collector will not recycle it. 因此,在堆栈帧消失之后,匿名类要么具有原始类型的副本,要么具有其实际值的副本(其值存储在堆中),并且当存在对对象的引用时,垃圾回收器将不会对其进行回收。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM