有时候,我们希望自己的程序在某台服务器上只启动一个进程,启动多个进程可能会引起一些问题。
两种方式可以实现:
- 端口占用冲突
- 文件排他锁
web项目可以通过端口冲突来实现,非web项目可以通过文件排他锁实现。代码示例如下:
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
@Slf4j
public class ProcessLock {
private static final String FILE_NAME = "process.lock";
private static FileChannel fileChannel;
public static boolean tryLock(String lockFileDir) {
log.info("lockFileDir: {}", lockFileDir);
try {
String dir = lockFileDir;
if (StringUtils.isBlank(dir)) {
dir = "./";
} else {
if (dir.charAt(dir.length() - 1) != '/') {
dir = dir + "/";
}
}
File file = new File(dir + FILE_NAME);
if (!file.exists()) {
if (!file.createNewFile()) {
log.error("Create file " + file.getAbsolutePath() + " fail");
return false;
}
}
RandomAccessFile raf = new RandomAccessFile(file, "rw");
fileChannel = raf.getChannel();
FileLock fileLock = fileChannel.tryLock();
if (fileLock == null) {
log.info("fileLock is null");
return false;
}
return fileLock.isValid();
} catch (Exception ex) {
log.error("tryLock fail", ex);
}
return false;
}
public static void unlock() {
if (fileChannel != null) {
try {
fileChannel.close();
} catch (IOException ex) {
log.error("", ex);
}
}
}
}
这里有几点需要注意:
- 进程启动时tryLock,进程退出时unlock
- lockFileDir一定要用绝对路径。如果用相对路径,很容易因为启动目录不同而导致加锁失败,因为每个进程用的不是同一个文件
- fileChannel一定要在进程的生命周期内一直持有并且不能close,否则无法实现互斥