【问题标题】:Cross process synchronization in JavaJava中的跨进程同步
【发布时间】:2011-03-14 11:15:16
【问题描述】:

如何同步在 Windows 上运行的两个 Java 进程?

我正在寻找类似 Win32 Named Mutex 对象的东西,它允许两个进程使用相同的锁定对象。

谢谢

【问题讨论】:

  • 我不确定同步进程是个好主意。你有没有机会解释你为什么需要它?我想不出这种同步的单一用途。
  • 如果您有多个需要共享数据/锁的任务,它们应该在同一个 JVM 中。与 C/C++ 相比,在 Java 中将单个进程分解为多个进程的理由要少得多。
  • 例如,如果我需要超过 1.6 GB 的计算,我将需要多个进程(在 32 位机器上)。
  • 是的,但如果您启动 2 个 1.6GB 的进程,您已经超出了 Windows 可寻址空间。即使您启动 1 个具有大内存的进程,也不太可能启动另一个进程。除了套接字(或可能是文件)之外,您几乎没有其他选择。

标签: java windows multithreading


【解决方案1】:

我认为 java 平台中没有用于此的本地方法。但是,有几种方法可以获得相同类型的效果,具体取决于您尝试完成的同步。除了让进程通过网络连接(直接套接字、多播和选举等)进行通信或放弃特定于平台的调用之外,您还可以探索获取共享文件的文件锁定(参见 activemq 被动备用与以共享文件系统为例)或使用数据库,例如选择更新或表行的乐观更新。

【讨论】:

    【解决方案2】:

    我们使用这些语句来确保只有一个进程可以执行由“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);
        }
      }
    
    }
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        在 Java 中不可能做你想做的事情。不同的 Java 应用程序将使用不同的 JVM 将自己完全分离到不同的“黑盒”中。但是,您有 2 个选择:

        1. 使用套接字(或通道)。基本上,一个应用程序将打开侦听套接字并开始等待,直到它收到一些信号。另一个应用程序将在那里连接,并在完成某事后发送信号。我想说这是 99.9% 的应用程序中使用的首选方式。
        2. 您可以从 Java 调用 winapi。我不记得具体细节了,但是如果你用谷歌搜索“java winapi”,你可以找到很多例子。

        【讨论】:

        • 它可能是两个不同的 JVM 的事实使它变得有趣。谢谢!
        【解决方案5】:

        我简化了Java42 answer

        用法

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

        【讨论】:

          【解决方案6】:

          不确定您要做什么,我可能会通过 JMX 公开某些内容并让单独的进程设置一个状态标志,然后以编程方式将您的线程从等待状态中恢复过来。您当然可以使用套接字/RMI 来代替 JMX。

          【讨论】:

            【解决方案7】:

            使用套接字进行跨进程同步是常见的做法。不仅适用于 java 应用程序,因为在大多数 *nix 环境中,我们没有 Windows 中的系统范围互斥锁。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-06-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多