2

我正在尝试启动 JMXConnectorServer 以进行管理和调试。但我不希望此服务在最后一个非守护线程终止时阻止应用程序正常退出。

换句话说,我希望以下程序立即终止:

public class Main {
    public static void main(final String[] args) throws IOException {
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        JMXServiceURL jmxUrl = new JMXServiceURL("rmi", null, 0);
        JMXConnectorServer connectorServer =
            JMXConnectorServerFactory.newJMXConnectorServer(jmxUrl, null, mbs);
        connectorServer.start();
    }
}
4

5 回答 5

1

是的,你需要connectorServer.stop();在某个时候这样做。

编辑:

在阅读您的评论时,听起来您应该执行以下操作:

connectorServer.start();
try {
    // create thread-pool
    ExecutorService threadPool = Executors...
    // submit jobs to the thread-pool
    ...
    threadPool.shutdown();
    // wait for the submitted jobs to finish
    threadPool.awaitTermination(Long.MAX_LONG, TimeUnit.SECONDS);
} finally {
    connectorServer.stop();
}

@Nicholas 关闭挂钩的想法是一个很好的想法。然而,通常情况下,我让我的线程等待从JMX 操作main设置的某种变量。shutdown()就像是:

public CountDownLatch shutdownLatch = new CountDownLatch(1);
...

// in main
connectorServer.start();
try {
    // do the main-thread stuff
    shutdownLatch.await();
} finally {
    connectorServer.stop();
}

// in some JMX exposed operation
public void shutdown() {
    Main.shutdownLatch.countDown();
}

顺便说一句,您可以使用我的SimpleJMX包来管理您的 JMX 服务器。

JmxServer jmxServer = new JmxServer(8000);
jmxServer.start();
try {
    // register our lookupCache object defined below
    jmxServer.register(lookupCache);
    jmxServer.register(someOtherObject);
} finally {
    jmxServer.stop();
}
于 2012-09-28T18:17:49.760 回答
1

我玩过类似的问题并写了这个类:

public final class HardDaemonizer extends Thread {

    private final Runnable target;
    private final String newThreadName;

    public HardDaemonizer(Runnable target, String name, String newThreadName) {
        super(name == null ? "Daemonizer" : name);
        setDaemon(true);
        this.target = target;
        this.newThreadName = newThreadName;
    }

    @Override
    public void run() {
        try {
            List<Thread> tb = getSubThreads();
            target.run();
            List<Thread> ta = new java.util.ArrayList<>(getSubThreads());
            ta.removeAll(tb);
            for (Thread thread : ta) {
                thread.setName(newThreadName);
            }
            Thread.sleep(Long.MAX_VALUE);
        } catch (InterruptedException ex) {
            Logger.getLogger(HardDaemonizer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static Thread daemonize(String daemonizerName, String newThreadName, Runnable target) {
        HardDaemonizer daemonizer = new HardDaemonizer(target, daemonizerName, newThreadName);
        daemonizer.start();
        return daemonizer;
    }

    private static List<Thread> getSubThreads() {
        ThreadGroup group = Thread.currentThread().getThreadGroup().getParent();
        Thread[] threads = new Thread[group.activeCount()];
        group.enumerate(threads);
        return java.util.Arrays.asList(threads);
    }
}

你可以这样使用它:

HardDaemonizer.daemonize(null, "ConnectorServer", new Runnable(){
    @Override
    public void run() {
        try {
            connectorServer.start();
        } catch (IOException ex) {
            Logger.getLogger(Ralph.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
});

小心 - 这很棘手!


编辑

啊...这不是您的解决方案。它仅对连接器线程进行硬守护,当 jvm 停止时,该线程将被终止。另外,您可以自定义此线程的名称。

或者,您可以在方法中添加标志completed和睡眠循环,daemonize直到连接器服务器启动。


简化

这是简化的守护进程,没有棘手的线程重命名:

public abstract class Daemonizer<T> extends Thread {

    private final T target;
    private boolean completed = false;
    private Exception cause = null;

    public Daemonizer(T target) {
        super(Daemonizer.class.getSimpleName());
        setDaemon(true);
        this.target = target;
    }

    @Override
    public void run() {
        try {
            act(target);
        } catch (Exception ex) {
            cause = ex;
        }
        completed = true;
        try {
            Thread.sleep(Long.MAX_VALUE);
        } catch (InterruptedException ex) {
        java.util.logging.Logger.getLogger(Daemonizer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
    }

    public abstract void act(final T target) throws Exception;

    public static void daemonize(Daemonizer daemonizer) throws Exception {
        daemonizer.start();
        while (!daemonizer.completed) {
            Thread.sleep(50);
        }
        if (daemonizer.cause != null) {
            throw daemonizer.cause;
        }
    }
}

用法:

Daemonizer.daemonize(new Daemonizer<JMXConnectorServer>(server) {
    @Override
    public void act(JMXConnectorServer server) throws Exception {
        server.start();
    }
});
于 2012-10-31T04:06:07.167 回答
0

您可以添加一个JVM 关闭挂钩来停止连接器服务器。

===== 更新 =====

不知道为什么你的关机钩子不起作用。也许您可以提供您的示例代码。这是一个例子:

public static void main(String[] args) {        
    try {
        log("Creating Connector Server");
        final JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(new JMXServiceURL("rmi", "localhost", 12387), null, ManagementFactory.getPlatformMBeanServer());
        Thread jcsStopper = new Thread("JCS-Stopper") {
            public void run() {
                if(jcs.isActive()) {
                    try {
                        jcs.stop();
                        log("Connector Server Stopped");
                    } catch (Exception e) {
                        log("Failed to stop JCS");
                        e.printStackTrace();
                    }                       
                }
            }
        };
        jcsStopper.setDaemon(false);
        Runtime.getRuntime().addShutdownHook(jcsStopper);
        log("Registered Server Stop Task");
        jcs.start();
        log("Server Started");
        Thread.sleep(3000);
        System.exit(0);

    } catch (Exception ex) {
        ex.printStackTrace(System.err);
    }
}

输出是:

[main]:Creating Connector Server
[main]:Registered Server Stop Task
[main]:Server Started
[JCS-Stopper]:Connector Server Stopped
于 2012-09-28T18:13:31.527 回答
0
String port = getProperty("com.sun.management.jmxremote.port");
if (port == null) {
    port = String.valueOf(getAvailablePort());
    System.setProperty("com.sun.management.jmxremote.port", port);
    System.setProperty("com.sun.management.jmxremote.ssl", "false");
    System.setProperty("com.sun.management.jmxremote.authenticate", "false");

    sun.management.Agent.startAgent();
}
log.info(InetAddress.getLocalHost().getCanonicalHostName() + ":" + port);
于 2012-10-01T13:18:34.163 回答
0

根据我的经验,JMXConnectorServer当您明确创建它时,它只会在用户线程中运行。

如果您改为通过系统属性为平台 MBean 服务器配置 RMI 访问,则隐式创建的 JMX 连接器服务器将作为守护进程运行,并且不会阻止 JMV 关闭。为此,您的代码将缩小为以下

public class Main {
    public static void main(final String[] args) throws IOException {
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
    }
}

但您需要设置以下系统属性:

-Dcom.sun.management.jmxremote.port=1919
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
于 2014-04-24T13:51:47.013 回答