Сервер RMI не будет выполнять поток и умрет с помощью метода LocateRegistry.createRegistry

#java #rmi

#java #rmi

Вопрос:

Сейчас я использую LocateRegistry.createRegistry(1099) скорее, чем реестр во внешнем процессе. Однако реестр умирает после завершения основной программы. Например, если я создам простую программу, которая создает реестр, она не будет работать, потому что после основного выполнения код заканчивается. Я ожидал, что LocateRegistry код создаст поток, но, похоже, это не так. Это нормальное поведение при использовании LocateRegistry или я что-то упускаю?

Пример кода:

 // ommited imports

public class RMITest {
    public static void main(String[] args) {
        LocateRegistry.createRegistry(1099);
        // JVM will exit now!!!
    }
}
  

Сервер RMI запускается и внезапно умирает. Как

Ответ №1:

Я ожидал, что код LocateRegistry создаст поток

Все не так просто.

  1. Экспорт первого объекта на новый порт создает поток, который прослушивает этот порт, а неэкспортирование последнего объекта, прослушивающего порт, приводит к завершению этого потока. Это относится ко всем удаленным объектам, а не только к локальным объектам реестра.

  2. Отключение может происходить автоматически через локальный GC, который, в свою очередь, может быть запущен удаленным DGC.

Ваша JVM завершается, потому что вы не сохраняете значение, возвращаемое LocateRegistry.createRegistry() в статической переменной, поэтому оно получает GC’d, поэтому объект становится неэкспортированным, поэтому удаленные объекты не экспортируются на порт 1099, поэтому поток, который прослушивает 1099, завершается, поэтому нет потоков, не связанных с демоном, поэтому JVM завершается.

Решение: сохраните результат LocateRegistry.createRegistry() в статической переменной. Вы можете использовать это, чтобы отменить экспорт реестра, когда вы хотите, чтобы ваша JVM завершила работу.

Комментарии:

1. Это мне очень помогло. Во время сеанса отладки мне было интересно, почему было так много запущенных потоков RMI-демона. Причина: каждый раз, когда я запускал сервер, я экспортировал его заглушку на анонимный порт.

Ответ №2:

Есть два возможных способа запустить реестр RMI.

  1. LocateRegistry.createRegistry(1099); Приложение Java, выполняющее реестр, не должно завершаться. В вашем случае вы могли бы запустить новый «бесконечный» поток (исходный код см. Ниже)
  2. rmiregistry Это инструмент, включенный в дистрибутив Java, который запускает службу реестра RMI. смотрите rmiregistry — реестр удаленных объектов Java

Пример кода для сервера реестра RMI.

 import java.io.IOException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

public class RmiTest {

    public static void main(String[] args) throws IOException {
        final Object monitor = new Object();

        new Thread(new Runnable() {
            public void run() {
                try {
                    LocateRegistry.createRegistry(1099);
                    synchronized (monitor) {
                        monitor.wait();                        
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("RMI Registry Thread finished.");
            }
        }, "RMI Registry Thread").start();
        System.out.println("Press enter to exit...");
        System.in.read();
        synchronized (monitor) {
            monitor.notify();            
        }
    }
}
  

Комментарии:

1. Ваш код уязвим для ложных пробуждений. Ожидающий поток может прекратить ожидание, даже если уведомление не было вызвано. Вы должны выполнить цикл и проверить наличие флага, а основной поток должен изменить значение флага и уведомить. Или вы могли бы использовать CountDownLatch, инициализированный значением 1. Обратите внимание, что я не вижу смысла запускать реестр в новом потоке, который блокируется, поскольку основной поток также заблокирован.

2. Вам все это не нужно, и я сомневаюсь, что это действительно решает проблему. Смотрите мой ответ.

3. Да, вы правы @EJP. Этого было бы достаточно: LocateRegistry.createRegistry(1099); System.out.println("Press enter to exit..."); System.in.read(); . Я подумал о приложении, которое сначала запускает свой собственный реестр, а затем, возможно, выполняет какой-то другой код…

4. @AndreasKnees вам все это тоже не нужно. Просто сохраните возвращаемое значение и не экспортируйте его, когда закончите.

Ответ №3:

 LocateRegistry.createRegistry(1099);
  

создает новый поток демона с именем RMI TCP Accept-1099 на моем компьютере. Этот поток по существу прослушивает новые соединения TCP / IP на 1099.

Потоки демонов автоматически уничтожаются при выходе из JVM. И в вашем случае JVM завершается, когда вы покидаете main() метод. Точнее — он завершается, когда больше нет потоков, не являющихся демонами, — и, по-видимому, в вашем приложении есть только один поток, не являющийся демоном (с именем main ).

Итак, у вас есть два варианта:

  • не позволяйте main() методу завершаться добавлением бесконечности sleep() .
  • создайте какой-нибудь поток, не относящийся к демону. Конечно, делайте это только тогда, когда поток действительно делает что-то полезное, а не предотвращает выход JVM.