在多线程的环境下,如果两个线程操作相同的竞争区,需要使用锁来保证线程安全。在Java中有多种选择,如Synchronized
关键字,CountDownLatch
等等。但是这些方式,在多进程的情况下,会失效。
那么在多进程情况下,我们怎么做进程同步呢?答案是文件锁。Java提供的FileLock
类,可以实现,下面来看看具体的用法。
FileLock API
public abstract FileLock lock(long position, long size, boolean shared) throws IOException;
public final FileLock lock() throws IOException;
public abstract FileLock tryLock(long position, long size, boolean shared) throws IOException;
public final FileLock tryLock() throws IOException;
方法:
-
lock
方法,会block
住线程,直到获取到锁。 -
tryLock
方法,不会block
,会直接返回,如果能获取到锁,则返回FileLock
对象,如果获取不到锁,返回null
。
参数:
-
position
:锁定文件中的开始位置 -
size
:锁定文件中的内容长度 -
shared
:是否使用共享锁。true
为共享锁;false
为独占锁。
- 共享锁:允许其他进程同时获取
- 独占锁:不允许其他进程同时获取
使用示例
进程1
public static void main(String[] args) {
try {
// 打开文件
FileOutputStream raf = new FileOutputStream("lock.txt");
// 获取FileChannel
FileChannel channel = raf.getChannel();
// 获取FileLock,上锁
FileLock lock = channel.lock();
Thread.sleep(10000);
// 手动释放FileLock
lock.close();
} catch (Exception e) {
e.printStackTrace();
}
}
进程1启动后,会获取FileLock
锁,然后持有锁Sleep 10s。
进程2:
public static void main(String[] args) {
try {
FileOutputStream raf = new FileOutputStream("lock.txt");
FileChannel channel = raf.getChannel();
// 会被block,直到获得锁
FileLock lock = channel.lock();
// 如果没有获得锁,直接返回null,不会被block
lock = channel.tryLock();
} catch (Exception e) {
e.printStackTrace();
}
}
此时进程2启动:
- 如果调用
lock
方法,会block
线程,直到进程1释放锁。 - 如果调用
tryLock
方法,不会block
,会直接返回,如果能获取到锁,则返回FileLock
对象,如果获取不到锁,返回null
。
总结
FileLock
可以用于Android
中多进程同步,有以下几点需要注意:
- 如果同一个进程中的不同线程,同时对一个文件进行加锁操作,会直接抛出异常:
java.nio.channels.OverlappingFileLockException
。 -
FileLock
释放的条件是,手动调用release/close
释放,或JVM终止运行。 - 本质也是调用
Linux
的fnctl
来从内核对文件进行加锁。 - 同一个
FileChannel
只能被使用一次,如果调用close
之后,再去调用lock
,会报ClosedChannelExcption
。