有没有办法终止使用java3d的java应用程序,而无需调用System.exit()? [英] Is there a way to terminate a java application that uses java3d, without calling System.exit()?

查看:205
本文介绍了有没有办法终止使用java3d的java应用程序,而无需调用System.exit()?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Java3D启动多个系统线程,并且不在它们上设置isDaemon标志。当我处置我的应用程序的(仅)JFrame时它不会终止,因为这些线程仍在运行。

Java3D starts several system threads and doesn't set the isDaemon flag on them. When I dispose the (only) JFrame of my application it won't terminate because these threads are still running.

调用System.exit()似乎是唯一的方法终止申请。 (或者当然从外面杀死它。)

Calling System.exit() seems to be the only way to terminate the application. (Or killing it from outside, of course).

由于我不喜欢调用System.exit(),我尝试了以下(但没有成功):

As I don't like to call System.exit() I have tried the following (but without success):


  • 在VirtualUniverse上调用removeAllLocales():这会终止大多数线程,但仍有一个(名为J3D-Renderer-1)使用反射来获取对javax.media.j3d.MasterControl中的字段ThreadGroup rootThreadGroupp的引用,并在该ThreadGroup上设置isDeamon为true。 b

  • 这似乎没有任何影响。

  • 创建一个名为Java3D的ThreadGroup的引用并在其上调用interrupt():这导致java3d线程将InterruptedException写入stderr,但没有别的。

  • 找到Java3d核心库的来源并提出补丁:我在这里找到了一个存储库: https://github.com/hharrison/java3d-core 和这里: https:/ /java.net/projects/j3d-core/sources 。后一个看起来是官方的,但显示它发生在5年前的最后一次改变,而前一个看起来像是私人分叉给我。

  • calling removeAllLocales() on the VirtualUniverse: This terminates most of the threads, but still there is one (named J3D-Renderer-1) remaining.
  • using reflection to obtain a reference to the the field ThreadGroup rootThreadGroupp in javax.media.j3d.MasterControl and settting isDeamon true on that ThreadGroup. This didn't seem to have any effect.
  • geting a reference to the ThreadGroup named "Java3D" and calling interrupt() on it: This caused the java3d threads to write InterruptedException to stderr, but nothing else.
  • locate the sources of the Java3d-core library and propose a patch: I found a repository here: https://github.com/hharrison/java3d-core and here: https://java.net/projects/j3d-core/sources . The later one looks "official" but shows the last change in it happened 5 years ago and the former one looks like a private fork to me.

我接近放弃并调用System.exit(),但我仍然不喜欢它。你知道更好的方法吗?

I am close to giving up and make that call to System.exit(), but I still don't like it. Do you know a better way?

推荐答案

一种可能的解决方案是调用 Java3dThread.finish() Java3D线程上的方法。缺点是必须绕过java访问规则来调用此方法,因为它是包私有的。这段代码对我有用:

One possible solution is to call the Java3dThread.finish() method on the Java3D threads. The drawback is that one has to bypass java access rules to call this method, as it is package-private. This code did the trick for me:

public void dispose() {
    virtualUniverse.removeAllLocales();
    try {
        // give the Java3D threads the chance to terminate peacefully.
        Thread.sleep(250);
    } catch (final InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    // and now handle the threads that didn't take the hint
    finishJava3dThreads();
}

private static void finishJava3dThreads() {
    final Class<?> rendererClass;
    final Method finishMethod;
    try {
        rendererClass = Class.forName("javax.media.j3d.J3dThread");
        finishMethod = rendererClass.getDeclaredMethod("finish");
        finishMethod.setAccessible(true);
    } catch (ClassNotFoundException | NoSuchMethodException | SecurityException e) {
        throw new RuntimeException(e);
    }
    final ThreadGroup[] groups = new ThreadGroup[10];
    final int count = Thread.currentThread().getThreadGroup().getParent().enumerate(groups);
    for (int i = 0; i < count; i++) {
        final ThreadGroup threadGroup = groups[i];
        if ("Java3D".equals(threadGroup.getName())) {
            threadGroup.setDaemon(true);
            final Thread[] threads = new Thread[threadGroup.activeCount()];
            final int threadCount = threadGroup.enumerate(threads);
            for (int j = 0; j < threadCount; j++) {
                final Thread thread = threads[j];
                if (rendererClass.isInstance(thread)) {
                    try {
                        finishMethod.invoke(thread);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            Thread.yield();
            threadGroup.interrupt();
        }
    }
}

其中virtualUniverse是实例VirtualUniverse之前在应用程序中创建。

Where virtualUniverse is the instance of the VirtualUniverse created earlier in the application.

现在我调用这个 dispose()方法来终止Java3D application:

Now I call this dispose() method to terminate Java3D when terminating the application:

   addWindowListener(new WindowAdapter() {
        @Override
        public void windowClosed(final WindowEvent e) {
            plater.dispose();
        }
    });

其中 plater 是包含<的实例code> dispose()上面的方法。

Where plater is the instance containing the dispose() method from above.

其他一切只是处理主JFrame:

Everything else just disposes the main JFrame:

    actions.put(EXIT_ACTION, new AbstractAction(EXIT_ACTION) {
        @Override
        public void actionPerformed(final ActionEvent e) {
            dispose();
        }
    });

并且默认关闭操作也设置为 DISPOSE_ON_CLOSE

and the default close operation is also set to DISPOSE_ON_CLOSE:

    setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

不确定这是否是最佳选择。 (我仍然喜欢它超过 System.exit()调用,但以这种方式使用反射有点脆弱。)

Not sure if this is the best option, though. (I still prefer it over the System.exit() call, but using reflection in this way is somewhat fragile.)

这篇关于有没有办法终止使用java3d的java应用程序,而无需调用System.exit()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆