ScheduledExecutorService生命周期?(ScheduledExecutorService Life Cycle?)

编程入门 行业动态 更新时间:2024-10-24 07:24:40
ScheduledExecutorService生命周期?(ScheduledExecutorService Life Cycle?)

我有一个对象需要定期做一些工作,而对象本身还活着,所以我设计了如下内容。 基本上是一个Main类,它包含对ScheduledExecutorService实例的引用。 在这个例子中,所有的定期工作是打印一个字符串到std。

我期望代码的行为如下所示:

调用test2,它创建一个Main对象o1(在其中包含ScheduledExecutorService)。 test2寄存器在o1上每秒打印一行。 test2返回,o1变成垃圾。 系统gc启动gc o1,它有一个终止方法关闭它的本地调度程序。

但是,如果我运行这个程序,会发生什么,它会继续。 基本上gc永远不会调用o1的终结器,因此调度器永远不会关闭,因此,即使主线程结束,程序仍然不会退出。

现在,如果我在test2()中注释掉o1.register,那么程序的行为应该像它应该那样,例如gc等。在调试器中,只有在调用ScheduledExecutorService.schedule后才会创建一个实际的线程。

任何解释发生了什么?

public class Main { public static void main(String[] args) throws Exception { test2(); System.gc(); System.out.println("Waiting for finalize to be called.."); Thread.sleep(5000); } private static void test2() throws Exception { Main o1 = new Main(); o1.register(); Thread.sleep(5000); } private final ScheduledExecutorService _scheduler = Executors.newSingleThreadScheduledExecutor(); private void register() { _scheduler.scheduleWithFixedDelay(new Runnable() { @Override public void run() { System.out.println("!doing stuff..."); } }, 1, 1, TimeUnit.SECONDS); } @Override protected void finalize() throws Throwable { try { System.out.print("bye"); _scheduler.shutdown(); } finally { super.finalize(); } }

}

I have an object that needs to do periodically do some work while the object itself is alive, so I designed something like the following. Basically a Main class which contains a reference to a ScheduledExecutorService instance. In this example, all the periodical work is to print a string to std.

I expect the code to behave like the following:

test2 gets called, which create a Main object o1 (within it a ScheduledExecutorService). test2 register to print out a line every second on o1. test2 returns, o1 becomes garbage. System gc kicks in to gc o1, which has a finalize method to shutdown it's local scheduler.

However, if I run this program, what happens is that it will go on FOREVER. Basically the gc never calls o1's finalizer and as a result, scheduler never shuts down and as a result, even when main thread end, the program still won't quit.

Now if I comment out the o1.register in test2(), the program behaves like it should, e.g. gc called etc. Also in debugger it seems only after a call to ScheduledExecutorService.schedule will an actual thread created.

Any explanation what's happening?

public class Main { public static void main(String[] args) throws Exception { test2(); System.gc(); System.out.println("Waiting for finalize to be called.."); Thread.sleep(5000); } private static void test2() throws Exception { Main o1 = new Main(); o1.register(); Thread.sleep(5000); } private final ScheduledExecutorService _scheduler = Executors.newSingleThreadScheduledExecutor(); private void register() { _scheduler.scheduleWithFixedDelay(new Runnable() { @Override public void run() { System.out.println("!doing stuff..."); } }, 1, 1, TimeUnit.SECONDS); } @Override protected void finalize() throws Throwable { try { System.out.print("bye"); _scheduler.shutdown(); } finally { super.finalize(); } }

}

最满意答案

两个问题:

默认线程工厂创建非守护线程。 主线程可以结束,但只要存在活动的非守护程序线程,JVM就不会终止。 我相信你将需要编写一个自定义线程工厂来创建守护进程线程。 不要依赖于被调用的终结器 - 不能保证在任何特定时间或任何时候都会调用终结器。 此外, System.gc()调用被定义为对JVM的建议 ,而不是命令。 API文档中的措辞是

调用gc方法表明Java虚拟机花费了对未使用对象进行回收的努力......

After playing with WeakReference and ScheduledExecutorService, I think I have a better understanding of the problem now. The central problem in my code is the following method register(). It uses an anonymous object Runnable. The problem with anonymous object like this is it creates a strong reference back to the parent scope. Remember if you make fields in parent scope "final", you can reference to them from within run() method of Runnable. I thought I do not create such strong ref if I don't reference anything from my run(). As shown in this case, all I do in the run() is to print some static string out. However, according the the behavior observed, such reference is created nonetheless.

private void register() { _scheduler.scheduleWithFixedDelay(new Runnable() { @Override public void run() { System.out.println("!doing stuff..."); } }, 1, 1, TimeUnit.SECONDS);

}

The correct way of doing this kind of programming is to create a class and pass in your object yourself. You also need to keep only a weak reference. The code is rather long, I'll just post the Runnable implementation, which keeps a weak reference to the domain object Main.

private static class ResourceRefreshRunner implements Runnable { WeakReference<Main> _weakRef; public ResourceRefreshRunner(Main o) { _weakRef = new WeakReference<Main>(o); } @Override public void run() { try { Main m = _weakRef.get(); if (m != null) m.shout(); else System.out.println("object not there, but future is running. "); } catch (Exception ex) { System.out.println(ex.toString()); } } }

Now in the Main class, I have:

public class Main { ScheduledExecutorService _poolInstance; ScheduledFuture<?> _future; public Main(ScheduledExecutorService p) { _poolInstance = p; _future = _poolInstance.scheduleWithFixedDelay(new ResourceRefreshRunner(this), 1, 1, TimeUnit.SECONDS); } ...

And the finalizer of Main:

@Override protected void finalize() throws Throwable { try { System.out.println("bye"); _future.cancel(true); } finally { super.finalize(); } }

With this setup, the code behaves as expected. E.g. when a Main object is no longer referenced, GC will kick in and the finalizer will get called. One more experiment I did is that without _future.cancel(true); in the finalize(), when the Main object is GC-ed, the weak reference in the Runnable.run() can't dereference to a Main object anymore, but the thread and tasks is still running.

更多推荐

本文发布于:2023-07-28 01:13:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1298254.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:生命周期   ScheduledExecutorService   Cycle   Life

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!