#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 создаст поток
Все не так просто.
-
Экспорт первого объекта на новый порт создает поток, который прослушивает этот порт, а неэкспортирование последнего объекта, прослушивающего порт, приводит к завершению этого потока. Это относится ко всем удаленным объектам, а не только к локальным объектам реестра.
-
Отключение может происходить автоматически через локальный GC, который, в свою очередь, может быть запущен удаленным DGC.
Ваша JVM завершается, потому что вы не сохраняете значение, возвращаемое LocateRegistry.createRegistry()
в статической переменной, поэтому оно получает GC’d, поэтому объект становится неэкспортированным, поэтому удаленные объекты не экспортируются на порт 1099, поэтому поток, который прослушивает 1099, завершается, поэтому нет потоков, не связанных с демоном, поэтому JVM завершается.
Решение: сохраните результат LocateRegistry.createRegistry()
в статической переменной. Вы можете использовать это, чтобы отменить экспорт реестра, когда вы хотите, чтобы ваша JVM завершила работу.
Комментарии:
1. Это мне очень помогло. Во время сеанса отладки мне было интересно, почему было так много запущенных потоков RMI-демона. Причина: каждый раз, когда я запускал сервер, я экспортировал его заглушку на анонимный порт.
Ответ №2:
Есть два возможных способа запустить реестр RMI.
LocateRegistry.createRegistry(1099);
Приложение Java, выполняющее реестр, не должно завершаться. В вашем случае вы могли бы запустить новый «бесконечный» поток (исходный код см. Ниже)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.