Java: 是否可以创建包含内部心跳线程的可垃圾回收对象?
Java: is it possible to create a garbage-collectable object that includes an internal heartbeat thread?
小问题:
是否可以创建一个 class,例如它包含一个内部无限线程(心跳)并且它的对象会自动被垃圾收集?
长问题:
我打算创建一个包含内部无限后台线程(例如心跳)的 class。虽然,这个 class 的对象不需要显式销毁,并且在不再引用时应被垃圾收集(此时,心跳也应该被销毁),类似于 C#。
问题是:Java RE 不会对这些对象进行垃圾回收,因为它们内部包含一个 运行 线程。这导致了一个不朽的对象。
示例:
示例 C# 代码(按预期工作):
using System;
using System.Threading;
namespace HeartbeatTest
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main execution started.");
executeMethod();
// Just wait to see some heartbeats
Thread.Sleep(5000);
// This shall garbage-collect myObject
GC.Collect();
Console.WriteLine("Main execution finished.");
}
private static void executeMethod()
{
Console.WriteLine("executeMethod() execution started.");
MyObjectWithHeartbeat myObject = new MyObjectWithHeartbeat();
Console.WriteLine("executeMethod() execution finished.");
}
}
class MyObjectWithHeartbeat
{
private Thread heartbeatThread;
public MyObjectWithHeartbeat()
{
heartbeatThread = new Thread(() =>
{
try
{
while (true)
{
Console.WriteLine("heartbeat...");
Thread.Sleep(1000);
}
}
catch (ThreadInterruptedException)
{
// finish heartbeat execution
}
});
heartbeatThread.Start();
}
~MyObjectWithHeartbeat()
{
Console.WriteLine("MyObjectWithHeartbeat destroy");
heartbeatThread.Interrupt();
}
}
}
C# 输出:
Main execution started.
executeMethod() execution started.
executeMethod() execution finished.
heartbeat...
heartbeat...
heartbeat...
heartbeat...
heartbeat...
MyObjectWithHeartbeat destroy
Main execution finished.
示例 Java 代码(不起作用):
package heartbeattest;
public class Main {
public static void main(String[] args) {
System.out.println("Main execution started.");
executeMethod();
// Just wait to see some heartbeats
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// This should garbage-collect myObject
System.gc();
System.out.println("Main execution finished.");
}
private static void executeMethod() {
System.out.println("executeMethod() execution started.");
MyObjectWithHeartbeat myObject = new MyObjectWithHeartbeat();
System.out.println("executeMethod() execution finished.");
}
}
class MyObjectWithHeartbeat {
private Thread heartbeatThread;
public MyObjectWithHeartbeat() {
heartbeatThread = new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
System.out.println("heartbeat...");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
heartbeatThread.start();
}
@Override
protected void finalize() throws Throwable {
System.out.println("MyObjectWithHeartbeat destroy");
heartbeatThread.interrupt();
super.finalize();
}
}
Java 输出:
Main execution started.
executeMethod() execution started.
executeMethod() execution finished.
heartbeat...
heartbeat...
heartbeat...
heartbeat...
heartbeat...
heartbeat...
Main execution finished.
heartbeat...
heartbeat...
heartbeat...
heartbeat...
heartbeat...
heartbeat...
...
(it keeps executing)
是否有一种架构模式可以克服此 JRE "thread keeps running because object exists - object not destroyed because thread is running" 死锁?
我们在 Java 中不会这样做。
通常的 Java 方法是:
- 创建和对象
- 创建线程并将该对象传递给线程
- 线程持有对象的弱引用
- 只要弱引用不为空,主循环就会运行
这是一些伪代码
class Heartbeat extends Thread {
WeakReference exists;
Heartbeat(Object alive) {
exists = new WeakReference(alive)
}
public void run() {
// if the object has not been GC'ed keep running
while (exists.get() != null) {
// do something
}
// otherwise terminate the thread
}
}
String alive = new String("hello!"); // explicit new, believe me!
Thread t = new Heartbeat(alive);
t.start();
p.s.: 请注意 System.gc() 不被授予垃圾收集任何东西
p.p.s.: 我们通常不会在 Java 世界中编写终结器,除非我们正在编写 infrastructure-level 库 :-)
小问题: 是否可以创建一个 class,例如它包含一个内部无限线程(心跳)并且它的对象会自动被垃圾收集?
长问题:
我打算创建一个包含内部无限后台线程(例如心跳)的 class。虽然,这个 class 的对象不需要显式销毁,并且在不再引用时应被垃圾收集(此时,心跳也应该被销毁),类似于 C#。
问题是:Java RE 不会对这些对象进行垃圾回收,因为它们内部包含一个 运行 线程。这导致了一个不朽的对象。
示例:
示例 C# 代码(按预期工作):
using System;
using System.Threading;
namespace HeartbeatTest
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main execution started.");
executeMethod();
// Just wait to see some heartbeats
Thread.Sleep(5000);
// This shall garbage-collect myObject
GC.Collect();
Console.WriteLine("Main execution finished.");
}
private static void executeMethod()
{
Console.WriteLine("executeMethod() execution started.");
MyObjectWithHeartbeat myObject = new MyObjectWithHeartbeat();
Console.WriteLine("executeMethod() execution finished.");
}
}
class MyObjectWithHeartbeat
{
private Thread heartbeatThread;
public MyObjectWithHeartbeat()
{
heartbeatThread = new Thread(() =>
{
try
{
while (true)
{
Console.WriteLine("heartbeat...");
Thread.Sleep(1000);
}
}
catch (ThreadInterruptedException)
{
// finish heartbeat execution
}
});
heartbeatThread.Start();
}
~MyObjectWithHeartbeat()
{
Console.WriteLine("MyObjectWithHeartbeat destroy");
heartbeatThread.Interrupt();
}
}
}
C# 输出:
Main execution started.
executeMethod() execution started.
executeMethod() execution finished.
heartbeat...
heartbeat...
heartbeat...
heartbeat...
heartbeat...
MyObjectWithHeartbeat destroy
Main execution finished.
示例 Java 代码(不起作用):
package heartbeattest;
public class Main {
public static void main(String[] args) {
System.out.println("Main execution started.");
executeMethod();
// Just wait to see some heartbeats
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// This should garbage-collect myObject
System.gc();
System.out.println("Main execution finished.");
}
private static void executeMethod() {
System.out.println("executeMethod() execution started.");
MyObjectWithHeartbeat myObject = new MyObjectWithHeartbeat();
System.out.println("executeMethod() execution finished.");
}
}
class MyObjectWithHeartbeat {
private Thread heartbeatThread;
public MyObjectWithHeartbeat() {
heartbeatThread = new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
System.out.println("heartbeat...");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
heartbeatThread.start();
}
@Override
protected void finalize() throws Throwable {
System.out.println("MyObjectWithHeartbeat destroy");
heartbeatThread.interrupt();
super.finalize();
}
}
Java 输出:
Main execution started.
executeMethod() execution started.
executeMethod() execution finished.
heartbeat...
heartbeat...
heartbeat...
heartbeat...
heartbeat...
heartbeat...
Main execution finished.
heartbeat...
heartbeat...
heartbeat...
heartbeat...
heartbeat...
heartbeat...
...
(it keeps executing)
是否有一种架构模式可以克服此 JRE "thread keeps running because object exists - object not destroyed because thread is running" 死锁?
我们在 Java 中不会这样做。 通常的 Java 方法是:
- 创建和对象
- 创建线程并将该对象传递给线程
- 线程持有对象的弱引用
- 只要弱引用不为空,主循环就会运行
这是一些伪代码
class Heartbeat extends Thread {
WeakReference exists;
Heartbeat(Object alive) {
exists = new WeakReference(alive)
}
public void run() {
// if the object has not been GC'ed keep running
while (exists.get() != null) {
// do something
}
// otherwise terminate the thread
}
}
String alive = new String("hello!"); // explicit new, believe me!
Thread t = new Heartbeat(alive);
t.start();
p.s.: 请注意 System.gc() 不被授予垃圾收集任何东西
p.p.s.: 我们通常不会在 Java 世界中编写终结器,除非我们正在编写 infrastructure-level 库 :-)