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

私は同様の問題で遊んで、このクラスを書きました:

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 に答える
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 に答える
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

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

私の経験から、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 に答える