Как обеспечить выполнение только одного экземпляра java-программы?

Требуется, чтобы только один экземпляр программы JAVA мог быть выполнен в определенное время. Я наблюдал большое количество решений, предлагаемых в переполнении стека в разных предыдущих постах.

Решения основаны на:

  • Открытие гнезда: открытие соединения сокета.
  • На основе блокировки файлов: создать временный файл и удерживать блокировку. И добавить кран отключения, чтобы разблокировать этот файл, когда JVM отключится.

Я не хочу использовать блокировку портов, поскольку это может вызвать конфликт в использовании портов.

Итак, я думал использовать блокировку файлов. После некоторого поиска я обнаружил, что сторонники механизма блокировки портов упомянули, что блокировка файлов может быть ненадежной, если сбой приложений и другие ошибки ввода-вывода.

Мне нужно найти решение, которое будет работать последовательно в кросс-платформе и в нескольких JDK. Моя целевая платформа - Windows и Linux, а JDK - Sun и IBM JDK.

Может ли кто-нибудь пролить свет на это?

Ответ 1

Вы можете использовать объект ManagementFactory. Из здесь: -

import sun.management.ConnectorAddressLink;  
import sun.jvmstat.monitor.HostIdentifier;  

import sun.jvmstat.monitor.Monitor;  
import sun.jvmstat.monitor.MonitoredHost;  

import sun.jvmstat.monitor.MonitoredVm;  
import sun.jvmstat.monitor.MonitoredVmUtil;  
import sun.jvmstat.monitor.MonitorException;  
import sun.jvmstat.monitor.VmIdentifier;  

public static void main(String args[]) {  
/* The method ManagementFactory.getRuntimeMXBean() returns an identifier with applcation PID
   in the Sun JVM, but each jvm may have you own implementation. 
   So in anothers jvm, other than Sun, this code may not work., :( 
*/  
 RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean();  
         final int runtimePid = Integer.parseInt(rt.getName().substring(0,rt.getName().indexOf("@")));  

  java.awt.EventQueue.invokeLater(new Runnable() {  
  public void run() {  

  // If exists another instance, show message and terminates the current instance.  
  // Otherwise starts application.  
  if (getMonitoredVMs(runtimePid))  
  {  
     new MainFrame().setVisible(true);  
  } else  
  JOptionPane.showMessageDialog(null,"There is another instance of this application running.");  

  }  
  });  
  }

Метод getMonitoredVMs (int processPid) получает в качестве параметра текущий PID приложения и уловить имя приложения, которое вызывается из командной строки, например, приложение было запущено из c:\java\app\test.jar, тогда переменная значения "C:\Java\приложение\teste.jar". Таким образом, мы поймаем только имя приложения на строке 17 приведенного ниже кода. После этого мы ищем JVM для antoher с тем же именем, если мы его нашли и приложение PID отличается, это означает, что это второй экземпляр приложения.

private static boolean getMonitoredVMs(int processPid) {  
         MonitoredHost host;  
         Set vms;  
try {  
     host = MonitoredHost.getMonitoredHost(new HostIdentifier((String)null));  
     vms = host.activeVms();  
    } catch (java.net.URISyntaxException sx) {  
 throw new InternalError(sx.getMessage());  
  } catch (MonitorException mx) {  
 throw new InternalError(mx.getMessage());  
 }  
 MonitoredVm mvm = null;  
 String processName = null;  
 try{  
     mvm = host.getMonitoredVm(new VmIdentifier(String.valueOf(processPid)));  
     processName = MonitoredVmUtil.commandLine(mvm);  
     processName = processName.substring(processName.lastIndexOf("\\") + 1,processName.length());  
             mvm.detach();  
     } catch (Exception ex) {  

     }  
 // This line is just to verify the process name. It can be removed. 
  JOptionPane.showMessageDialog(null,processName);  
  for (Object vmid: vms) {  
  if (vmid instanceof Integer) {  
  int pid = ((Integer) vmid).intValue();  
  String name = vmid.toString(); // default to pid if name not available  
  try {  
      mvm = host.getMonitoredVm(new VmIdentifier(name));  
      // use the command line as the display name  
    name =  MonitoredVmUtil.commandLine(mvm);  
    name = name.substring(name.lastIndexOf("\\")+1,name.length());  
    mvm.detach();  
    if ((name.equalsIgnoreCase(processName)) && (processPid != pid))  
    return false;  
   } catch (Exception x) {  
   // ignore  
   }  
   }  
   }  

   return true;  
   }

Также проверьте Использование службы SingleInstanceService

javax.jnlp.SingleInstanceService предоставляет набор методов для приложения для регистрации себя в качестве одиночек и регистрации слушателя (ов) для обработки аргументов, переданных из разных экземпляров приложений.

Ответ 2

Если вы знакомы с C, вы можете решить эту проблему с помощью именованных каналов в Windows и локальном сокете в unix. Оба они требуют немного JNI. Эти каналы связи являются ресурсами вашего приложения, поэтому, когда ваше приложение выходит из строя, ОС несет ответственность за освобождение ваших ресурсов. Кроме того, они идентифицируются текстовым именем, поэтому вероятность столкновения имен одинакова для блокировки файлов.

Вы можете выбрать пример локального сокета в этом fooobar.com/questions/446871/....

Пример именованного канала в окнах можно найти здесь

Ответ 3

Привет, есть много способов сделать это, просто зайдите на эту страницу. Я просто скопирую пасту. также посмотреть эту тему [StackOverflow] [1]

Один из самых обсуждаемых вопросов в java-мире - как сделать java приложение как один экземпляр.

Я google и нашел множество методов. Я размещаю здесь некоторые из популярных методов. Просто продолжайте и свяжитесь, если у вас есть проблемы......

  • Захват порта или через ServerSocket (короткий код). По этому методу мы создаем объект класса java.net.ServerSocket. И, пройдя номер порта, мы будем захвачены в первом экземпляре, поэтому что, если появился другой экземпляр, он бросает исключение связывания и вы можете отслеживать, что в системе запускается еще один экземпляр.

Просто посмотрите ссылку для кода http://yuvadevelopers.dmon.com/java_examples/Single_Instance_small.htm

  1. Захват порта или через ServerSocket (большой код). Это то же самое, что и первый метод, но в то время как google я получил этот большой код с другой вариант просто проходит код.

Просто посмотрите ссылку для кода См. Исходный источник здесь, из Google http://www.rbgrn.net/blog/2008/05/java-single-application-instance.html

  1. Доступ к файлу из локальной файловой системы. Это еще один способ сделать то же самое. Но это не так уж желательно, потому что когда-то JVM сбоев или из-за некоторой ошибки ввода-вывода, тогда файл не удаляется с жесткого диска. note: - Не помещайте свой файл (вы можете использовать любой файл) на диск C или где существует ОС.
    Просто см. Ниже код
/*
* Program for setting single instance in JAVA
* Copyright 2009 @ yuvadeveloper
* Code By:- Prashant Chandrakar
*
*/
import java.net.ServerSocket;
import javax.swing.JOptionPane;
import javax.swing.JFrame;
import java.io.IOException;
import java.net.BindException;
class SingleInstance
{
  public static ServerSocket serverSocket;
  public static String errortype = "Access Error";
  public static String error = "Application already running.....";
  public static void main(String as[])
  {
    try
    {
        //creating object of server socket and bind to some port number serverSocket = new ServerSocket(15486);
        ////do not put common port number like 80 etc.
        ////Because they are already used by system
        JFrame jf = new JFrame();
        jf.setVisible(true);
        jf.setSize(200, 200);
     }
     catch (BindException exc)
     {
        JOptionPane.showMessageDialog(null, error, errortype, JOptionPane.ERROR_MESSAGE);
        System.exit(0);
     }
     catch (IOException exc)
     {
        JOptionPane.showMessageDialog(null, error, errortype, JOptionPane.ERROR_MESSAGE);
        System.exit(0);
     }
   }
}
  1. Используя пакет java sun.jvmstat из tools.jar.

Просто просмотрите ссылку для кода

  1. Используя приложение Launch4j. Это сторонний инструмент для создания EXE для вашего приложения. Это дает вам возможность создания приложения с одним экземпляром. Просто попробуйте. Это идеальный инструмент.

Просто посмотрите приложение приложения launch4j http://launch4j.sourceforge.net/

Ответ 4

Как часто это делается в системах unix, необходимо создать файл, который является атомной операцией, а затем проверить, может ли файл быть создан или нет. Если бы было возможно создать файл, то этот процесс имеет блокировку и разрешен для запуска. Если файл не может быть создан, кто-то должен иметь блокировку, и экземпляр немедленно завершается. Код для такой вещи

private boolean lock()
 {
   try
    {
        final File file=new File("bpmdj.lock");
        if (file.createNewFile())
        {
            file.deleteOnExit();
            return true;
        }
        return false;
    }
    catch (IOException e)
    {
        return false;
    }
}

в главном приложении, которое вы начинаете с

    if (!lock())
    {
        System.out.println("Cannot lock database. Check that no other instance of BpmDj is running and if so, delete the file bpmdj.lock");
        return;
    }

Конечно, есть два оговорки, которые следует упомянуть. Прежде всего: если приложение сильно сработает, файл, скорее всего, не будет удален, что приведет к некоторым неудобствам для пользователя (ему нужно будет удалить сам файл блокировки).

Во-вторых: java documentation заявляет следующее:

createNewFile атомарно создает новый пустой файл... если и только если файл с этим именем еще не существует. Проверка на наличие файла и создание файла, если оно не существуют одна операция, которая является атомарной по отношению ко всем другим действия файловой системы, которые могут повлиять на файл. Примечание: этот метод не следует использовать для блокировки файлов, поскольку результирующий протокол не может чтобы они работали надежно. Необходимо использовать средство FileLock вместо этого.

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

Ответ 5

Вы можете использовать библиотеку JUnique. Он обеспечивает поддержку для запуска Java-приложения с одним экземпляром и является открытым исходным кодом. Он основан на блокировке файлов, но использует также случайный порт для отправки/получения сообщений из других запущенных java-экземпляров.

http://www.sauronsoftware.it/projects/junique/

См. также мой полный ответ на Как реализовать одно приложение Java Java?