13

How can I synchornize two Java processes running on Windows ?

I am looking for something like the Win32 Named Mutex object which allows two processes to use the same locking object.

Thanks

Saar
  • 1,753
  • 6
  • 20
  • 32
  • 3
    I'm not sure that synchronizing processes is a good idea. Any chance you could explain why you need that? I can't think of a single use of such synchronization. – bezmax Mar 14 '11 at 11:22
  • 1
    If you have multiple tasks which need to share data/locks they should be in the same JVM. There is far less reason to break up a single process into multiple processes in Java compared with C/C++. – Peter Lawrey Mar 14 '11 at 11:31
  • For instance, if I need calculations that require more then 1.6 gb I would need several processes (on a 32bit machine). – Saar Mar 16 '11 at 06:33
  • yes but if you start even 2 processes w/ 1.6GB you are already out of the Windows addressable space. Even if you start 1 process w/ large memory it'd be unlikely to start another. Besides sockets (or possibly files), you're virtually out of options. – bestsss Mar 18 '11 at 17:37

7 Answers7

18

Java cross process lock:

// 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();
        }
    });
}    
Java42
  • 7,628
  • 1
  • 32
  • 50
7

I simplified Java42 answer

Usage

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

The code in successLockRunnable will lock any other process on the same machine using this implementation.

Source

/**
 * 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();
            }
        }
    }
}
Community
  • 1
  • 1
Ilya Gazman
  • 31,250
  • 24
  • 137
  • 216
6

It is not possible to do something like you want in Java. Different Java applications will use different JVM's fully separating themselves into different 'blackbox'es. However, you have 2 options:

  1. Use sockets (or channels). Basically one application will open the listening socket and start waiting until it receives some signal. The other application will connect there, and send signals when it had completed something. I'd say this is a preferred way used in 99.9% of applications.
  2. You can call winapi from Java. I do not remember the specifics, but you can find a plethora of example if you google "java winapi".
bezmax
  • 25,562
  • 10
  • 53
  • 84
3

We use these kinds of statements to make sure only one process can do a block of code keyed by "myLockKey":

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

Here, we make use of this class:

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);
    }
  }

}
1

I don't think there are native methods in the java platform for this. However, there are several ways to go about obtaining the same type of effect depending on what synchronization you are trying to accomplish. In addition to having the processes communicate over network connections (direct sockets, multicast with an election, etc.) or dropping to platform specific calls, you can also explore obtaining a file lock to a shared file (see activemq passive stand-by with a shared file system for an example) or using a database either with something like a select for update or an optimistic update of a table row.

philwb
  • 3,805
  • 19
  • 20
0

Not sure what you are trying to do, I'd possibly do this by exposing something via JMX and having the separate processes set a status flag which then programmatically revives your thread from a wait state. Instead of JMX you could of course use a socket/RMI.

vickirk
  • 3,979
  • 2
  • 22
  • 37
0

using sockets for cross processes synchronizations is common practice . not only for java applications because in most *nix environments we have not system-wide mutexes as we have in Windows.

andriy
  • 1