CompletableFuture.thenAccept 中使用的垃圾回收对象的可用性
Availability of objects for garbage collection used inside CompletableFuture.thenAccept
我正在尝试从使用 CompletableFuture
的方法执行异步调用。完成该任务后,我尝试打印对象 DummyObject 的值,这些值对于调用 Async 调用的方法是本地的。
我想知道它是如何工作的?线程(myThread
)在 3 秒后死亡并且 DummyObject 超出范围,thenAccept 中的异步回调仍在打印正确的值。线程是否锁定了 DummyObject?或者发生了其他事情?
请注意,我只是想模拟一个场景。所以我正在使用 Thread.stop()
.
编辑 - 问题是关于 thenAccept Consumer 如何处理范围?请保留与此相关的答案。
以下程序的输出:
Starting Async stuff
Thread Alive? false
Reached Main end and waiting for 8 more seconds
James T. Kirk
United Federation of Planets
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
}
在您所说的那一点上,dummyObj
变量仍在您传递给 thenAccept
.[=15 的 lambda 中 范围内 =]
事实上,带有原始变量的栈帧已经消失了。但是,Java 编译器安排表示 lambda 的对象具有原始变量值的副本(即对 DummyObject
实例的引用)保存在只读合成变量中。
(如果变量 effectively final,lambda 只能在封闭方法中使用局部变量这一限制背后的原因。如果变量实际上是 final,它的值可以复制到另一个 final 变量,而不会因变量更改而导致不一致的风险。)
无论如何,lambda 通过合成变量使用 DummyObject
。只要 lambda 可访问,DummyObject
实例将保持可访问。
第一件事。请不要使用 stop() 方法。它已被弃用。线程在执行完其 运行 方法后自行终止。
现在回答您的问题,当您创建 CompletableFuture 时,它还会在 java 的 ForkJoinCommonPool
中创建一个线程,并在将来的某个时间在那里执行它。如此有效地在您的应用程序中创建了 3 个线程。所以你的对象不会死,因为它会被其中一个线程使用。
- 主线程
- 我的线程
- 最后一个是由 CompletableFuture 创建的。 (这将使用虚拟对象)
所以即使线程1、2死掉了。线程 3 仍将执行。
P.S。我已将您的线程命名为 Thread-MyThread 以向您展示这些线程。您可以通过调用线程的构造函数来为线程命名,该构造函数将线程的名称作为参数。我使用的工具是JVisualVM。
如果局部变量是原始类型,栈中存储的就是它的值。在引用类型局部变量的情况下,引用也存储在堆栈中,但实际对象存储在堆中。
因此,如果在匿名内部 class(代码中的 lambda)中使用局部变量,则堆栈中保存的内容的副本将被带到匿名 class,如果是原始类型变量副本是变量的值,在引用类型变量的情况下,副本是引用本身。因此,在堆栈帧消失后,匿名 class 要么拥有原始类型的副本,要么拥有实际引用的副本,其值存储在堆中,并且当存在对对象的引用时,垃圾收集器将不会回收它。
我正在尝试从使用 CompletableFuture
的方法执行异步调用。完成该任务后,我尝试打印对象 DummyObject 的值,这些值对于调用 Async 调用的方法是本地的。
我想知道它是如何工作的?线程(myThread
)在 3 秒后死亡并且 DummyObject 超出范围,thenAccept 中的异步回调仍在打印正确的值。线程是否锁定了 DummyObject?或者发生了其他事情?
请注意,我只是想模拟一个场景。所以我正在使用 Thread.stop()
.
编辑 - 问题是关于 thenAccept Consumer 如何处理范围?请保留与此相关的答案。
以下程序的输出:
Starting Async stuff
Thread Alive? false
Reached Main end and waiting for 8 more seconds
James T. Kirk
United Federation of Planets
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
}
在您所说的那一点上,dummyObj
变量仍在您传递给 thenAccept
.[=15 的 lambda 中 范围内 =]
事实上,带有原始变量的栈帧已经消失了。但是,Java 编译器安排表示 lambda 的对象具有原始变量值的副本(即对 DummyObject
实例的引用)保存在只读合成变量中。
(如果变量 effectively final,lambda 只能在封闭方法中使用局部变量这一限制背后的原因。如果变量实际上是 final,它的值可以复制到另一个 final 变量,而不会因变量更改而导致不一致的风险。)
无论如何,lambda 通过合成变量使用 DummyObject
。只要 lambda 可访问,DummyObject
实例将保持可访问。
第一件事。请不要使用 stop() 方法。它已被弃用。线程在执行完其 运行 方法后自行终止。
现在回答您的问题,当您创建 CompletableFuture 时,它还会在 java 的 ForkJoinCommonPool
中创建一个线程,并在将来的某个时间在那里执行它。如此有效地在您的应用程序中创建了 3 个线程。所以你的对象不会死,因为它会被其中一个线程使用。
- 主线程
- 我的线程
- 最后一个是由 CompletableFuture 创建的。 (这将使用虚拟对象)
所以即使线程1、2死掉了。线程 3 仍将执行。
P.S。我已将您的线程命名为 Thread-MyThread 以向您展示这些线程。您可以通过调用线程的构造函数来为线程命名,该构造函数将线程的名称作为参数。我使用的工具是JVisualVM。
如果局部变量是原始类型,栈中存储的就是它的值。在引用类型局部变量的情况下,引用也存储在堆栈中,但实际对象存储在堆中。
因此,如果在匿名内部 class(代码中的 lambda)中使用局部变量,则堆栈中保存的内容的副本将被带到匿名 class,如果是原始类型变量副本是变量的值,在引用类型变量的情况下,副本是引用本身。因此,在堆栈帧消失后,匿名 class 要么拥有原始类型的副本,要么拥有实际引用的副本,其值存储在堆中,并且当存在对对象的引用时,垃圾收集器将不会回收它。