Запрет запуска второго экземпляра приложения на Java
Очень часто при разработке ПО требуется сделать так, что бы в один момент времени была запущен только один экземпляр приложения. В некоторых языках программирования в объектах синхронизации реализована такая возможность. В Java нативные библиотеки не предоставляют подобного функционала. В интернете есть информация о 2х основных подходах для реализации данной задачи: с использованием лока на файл или локом порта. Поскольку доступные примеры кода не достаточно хороши я сделал свою реализацию блока запуска более одного экземпляра приложения.
От использования идеи лока порта я решил сразу отказаться, что бы предотвратить возможные проблемы взаимодействия с другими программами, которые работают с портами. Как вы уже догадались реализация лока будет выполняться через файл.
Почему то везде предлагают использовать лок-файл, который находится в рабочей директории программы. Да в принципе это удобно, но не стоит забывать то, что если будет две копии программы в разных директориях, то лок не сработает. Так же нет гарантии того, что будет доступ на запись в директорию где находится программа.
Я решил это таким способом: во временной директории создается файл и на него устанавливается Lock. Имя файла генерируется на основании ключа, который передается при создании лока. Другие запущенные экземпляры программы (либо другие программы использующие тот же ключ лока) проверяют есть этот файл в директории и установлен ли на него Lock.
Использование моего класса очень простое.
// Устанавливаем LOCK //
if (!AppLock.setLock("MY_CUSTOM_LOCK_KEY")) {
throw new RuntimeException("Only one application instance may run at the same time!");
}
// YOUR CODE
}
finally{
AppLock.releaseLock(); // Убираем лок
}
Сам класс AppLock работает только через 2 статических метода: setLock и releaseLock. Думаю, что у вас не возникнет особых проблем с его использованием. Этот код кроссплатформенный, я проверял его работу на Windows и Linux.
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* AppLock
* @author http://fresh2L.com
* @version 1.0
*/
public class AppLock
{
/** Закрытый конструктор */
private AppLock() { }
/**
* Файл на который цепляется Lock
*/
File lock_file = null;
FileLock lock = null;
FileChannel lock_channel = null;
FileOutputStream lock_stream = null;
/**
* Создает экземпляр класса
* @param key Ключ программы - пользовательское значение
*/
private AppLock(String key) throws Exception
{
// TODO: Может быть как-то переменную key проверять на валидность т.к. из нее имя файла может формироваться ?
String tmp_dir = System.getProperty("java.io.tmpdir");
if (!tmp_dir.endsWith(System.getProperty("file.separator"))) {
tmp_dir += System.getProperty("file.separator");
}
// Получаем MD5 ключа и создаем классный URI
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
md.reset();
String hash_text = new java.math.BigInteger(1, md.digest(key.getBytes())).toString(16);
// Как правило нули в начале строки урезаются этот код добавляет нули обратно
while (hash_text.length() < 32) {
hash_text = "0" + hash_text;
}
lock_file = new File(tmp_dir + hash_text + ".app_lock");
}
catch (Exception ex) {
}
// Если глюкнул MD5 попробуем просто так создать URI на основе ключа
if (lock_file == null) {
lock_file = new File(tmp_dir + key + ".app_lock");
}
lock_stream = new FileOutputStream(lock_file);
String f_content = "Java AppLock Object\r\nLocked by key: " + key + "\r\n";
lock_stream.write(f_content.getBytes());
lock_channel = lock_stream.getChannel();
lock = lock_channel.tryLock();
if (lock == null) {
throw new Exception("Can't create Lock");
}
}
/**
* Убираем Lock. Теперь другое приложение сможет его заново установить.
* @throws Throwable
*/
private void release() throws Throwable
{
if (lock != null) {
lock.release();
}
if (lock_channel != null) {
lock_channel.close();
}
if (lock_stream != null) {
lock_stream.close();
}
if (lock_file != null) {
lock_file.delete();
}
}
@Override
protected void finalize() throws Throwable
{
this.release();
super.finalize();
}
private static AppLock instance;
/**
* Устанавливает Lock с указанным ключем
* Метод может вызываться только один раз, повторные вызовы игнорируются.
*
* @param key Ключ блока
*/
public static boolean setLock(String key)
{
if (instance != null) {
return true;
}
try {
instance = new AppLock(key);
}
catch (Exception ex) {
instance = null;
Logger.getLogger(Application.class.getName()).log(Level.SEVERE, "Обламался AppLock", ex);
return false;
}
Runtime.getRuntime().addShutdownHook(new Thread()
{
@Override
public void run()
{
AppLock.releaseLock();
}
});
return true;
}
/**
* Как бы убирает Lock.
* После вызова повторое использование AppLock будет невозможно.
*/
public static void releaseLock()
{
if (instance == null) {
return;
}
try {
instance.release();
}
catch (Throwable ex) {
Logger.getLogger(AppLock.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Многие сейчас не знают как зарегистрироваться в контакте, а все потому, что эта социальная сеть недавно ввела новые правила регистрации пользователей в системе. Кто не успел - тот опоздал.


