Межпроцессная синхронизация в Java - PullRequest
9 голосов
/ 14 марта 2011

Как я могу синхронизировать два процесса Java, работающих в Windows?

Я ищу что-то вроде объекта именованного мьютекса Win32, который позволяет двум процессам использовать один и тот же объект блокировки.

Спасибо

Ответы [ 7 ]

14 голосов
/ 06 марта 2012

Межпроцессная блокировка Java:

// Tester
try {
    if (crossProcessLockAcquire(SomeClassInYourApp.class, 3000)) {
       // Success - This process now has the lock. (Don't keep it too long.)
    }
    else {
       // Fail (Timeout) - Another process still had the lock after 3 seconds.
    }
} finally {
    crossProcessLockRelease(); // try/finally is very important.
}

// Acquire - Returns success ( true/false )
private static boolean crossProcessLockAcquire(final Class<?> c, final long waitMS) {
    if (fileLock == null && c != null && waitMS > 0) {
        try {
            long dropDeadTime = System.currentTimeMillis() + waitMS;
            File file = new File(lockTempDir, c.getName() + ".lock");
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
            FileChannel fileChannel = randomAccessFile.getChannel();
            while (System.currentTimeMillis() < dropDeadTime) {
                fileLock = fileChannel.tryLock();
                if (fileLock != null) {
                    break;
                }
                Thread.sleep(250); // 4 attempts/sec
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return fileLock == null ? false : true;
}

// Release
private static void crossProcessLockRelease() {
    if (fileLock != null) {
        try {
            fileLock.release();
            fileLock = null;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

// Some class vars and a failsafe lock release.
private static File lockTempDir = new File(System.getProperty("java.io.tmpdir") + File.separator + "locks");
private static FileLock fileLock = null;
static {
    Runtime.getRuntime().addShutdownHook(new Thread() {
        public void run(){
            crossProcessLockRelease();
        }
    });
}    
5 голосов
/ 13 июня 2016

Я упростил Java42 ответ

Использование

ProcessLock lock = new ProcessLock("lockKey");
lock.run(successLockRunnable, timeOutLockRunnable);

Код в successLockRunnable заблокирует любой другой процесс на той же машине, используя этуреализация.

Источник

/**
 * Created by Ilya Gazman on 13/06/2016.
 * Based on https://stackoverflow.com/a/9577667/1129332
 */
public class ProcessLock {
    // Some class vars and a fail safe lock release.
    private File lockTempDir = new File(System.getProperty("java.io.tmpdir") + File.separator + "locks");
    private FileLock fileLock = null;
    private String key;

    public ProcessLock() {
        this("lock");
    }

    public ProcessLock(String key) {
        this.key = key;
        Runtime.getRuntime().addShutdownHook(new Thread() {
            public void run() {
                crossProcessLockRelease();
            }
        });
    }

    public void run(Runnable successCallback) {
        run(successCallback, null);
    }

    public void run(Runnable successCallback, Runnable timeOutCallback) {
        try {
            if (crossProcessLockAcquire(3000)) {
                successCallback.run();
            } else if (timeOutCallback != null) {
                timeOutCallback.run();
            }
        } finally {
            crossProcessLockRelease(); // try/finally is very important.
        }
    }

    // Acquire - Returns success ( true/false )
    private boolean crossProcessLockAcquire(final long waitMS) {
        if (fileLock == null && waitMS > 0) {
            try {
                long dropDeadTime = System.currentTimeMillis() + waitMS;
                File file = new File(lockTempDir, "_" + key + ".lock");
                RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
                FileChannel fileChannel = randomAccessFile.getChannel();
                while (System.currentTimeMillis() < dropDeadTime) {
                    fileLock = fileChannel.tryLock();
                    if (fileLock != null) {
                        break;
                    }
                    Thread.sleep(250); // 4 attempts/sec
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return fileLock != null;
    }

    // Release
    private void crossProcessLockRelease() {
        if (fileLock != null) {
            try {
                fileLock.release();
                fileLock = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
4 голосов
/ 14 марта 2011

Невозможно сделать что-то, как вы хотите в Java.Различные Java-приложения будут использовать разные JVM, полностью разделяясь на разные «черные ящики».Однако у вас есть 2 варианта:

  1. Использовать сокеты (или каналы).В основном одно приложение откроет сокет прослушивания и начнет ждать, пока не получит какой-либо сигнал.Другое приложение подключится туда и отправит сигналы, когда что-то завершит.Я бы сказал, что это предпочтительный способ, используемый в 99,9% приложений.
  2. Вы можете вызывать winapi из Java.Я не помню специфику, но вы можете найти множество примеров, если вы гуглите "java winapi".
0 голосов
/ 25 мая 2017

Мы используем эти виды операторов, чтобы убедиться, что только один процесс может выполнить блок кода с ключом «myLockKey»:

new LocalFileLock("myLockKey").doWithLock(() -> {
  doSomething();
  return null; // must return something
});    

Здесь мы используем этот класс:

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.function.Supplier;

import com.headlandstech.utils.FileUtils;
import com.headlandstech.utils.Log;

public class LocalFileLock {

  private final File lockFile;

  public LocalFileLock(String name) {
    this.lockFile = new File(FileUtils.TEMP_DIR, name + ".lock");
    if (!lockFile.isFile()) {
      FileUtils.writeStringToFile("", lockFile);
    }
  }

  public <T> T doWithLock(Supplier<T> f) {
    Log.log.info("Waiting on lock " + lockFile);
    try (FileChannel channel = new RandomAccessFile(lockFile, "rw").getChannel()) {
      final FileLock fileLock = channel.lock();
      Log.log.info("Lock " + lockFile + " obtained");
      T result = f.get();
      fileLock.release();
      Log.log.info("Lock " + lockFile + " released");
      return result;
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

}
0 голосов
/ 18 марта 2011

использование сокетов для кросс-процессов синхронизации является обычной практикой. не только для java-приложений, потому что в большинстве * nix-сред у нас нет общесистемных мьютексов, как в Windows.

0 голосов
/ 16 марта 2011

Я не думаю, что в java-платформе есть нативные методы для этого.Тем не менее, есть несколько способов получить один и тот же тип эффекта в зависимости от того, какую синхронизацию вы пытаетесь выполнить.В дополнение к тому, что процессы обмениваются данными по сетевым соединениям (прямые сокеты, многоадресная рассылка с выборами и т. Д.) Или сбрасывают вызовы, специфичные для платформы, вы также можете исследовать получение блокировки файла для общего файла (см. Пассивный режим ожидания activemq собщая файловая система для примера) или использование базы данных с чем-то вроде выбора для обновления или оптимистического обновления строки таблицы.

0 голосов
/ 14 марта 2011

Не уверен, что вы пытаетесь сделать, я, возможно, сделал бы это, выставив что-то через JMX и установив для отдельных процессов флаг состояния, который затем программно восстанавливает ваш поток из состояния ожидания. Вместо JMX вы можете использовать сокет / RMI.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...