android 跨进程的锁,跨进程文件锁 - FileChannel

本文详细探讨了在Java中如何利用FileChannel.lock实现文件操作的原子性,确保多进程间对文件的并发安全。通过分析源码,可以看到Java调用Linux的fnctl系统调用来加锁,确保文件的独占或共享访问。这一过程涉及到FileDispatcherImpl的native方法和底层的flock结构,以及F_SETLK和F_SETLKW命令的使用。
摘要由CSDN通过智能技术生成

背景

当有多个进程或者多个应用同时操作文件时 , 会并行往文件中写入字节 , 如何保证多个进程中文件写入或者操作当原子性就很重要.

此时 , 在Java层可以使用FileChannel.lock来完成多进程之间对文件操作的原子性 , 而该lock会调用Linux的fnctl来从内核对文件进行加锁

源码

通过File.getChannel.lock()将文件加锁

RandomAccessFile file;

file.getChannel().lock();

在getChannel中 , 调用FileChannelImpl.open打开文件

public final FileChannel getChannel() {

synchronized (this) {

if (channel == null) {

channel = FileChannelImpl.open(fd, path, true, rw, this);

}

return channel;

}

}

open函数中会创建FileDispathcerImpl对象 , 后续会使用它进行加锁

public static FileChannel open(FileDescriptor fd, String path,

boolean readable, boolean writable,

Object parent)

{

return new FileChannelImpl(fd, path, readable, writable, false, parent);

}

private FileChannelImpl(FileDescriptor fd, String path, boolean readable,

boolean writable, boolean append, Object parent)

{

this.fd = fd;

this.readable = readable;

this.writable = writable;

this.append = append;

this.parent = parent;

this.path = path;

this.nd = new FileDispatcherImpl(append);

// Android-changed: Add CloseGuard support.

if (fd != null && fd.valid()) {

guard.open("close");

}

}

4.在调用lock函数后 , 开始调用native方法锁住文件

public FileLock lock(long position, long size, boolean shared)

throws IOException

{

// 确认文件已经打开 , 即判断open标识位

ensureOpen();

if (shared && !readable)

throw new NonReadableChannelException();

if (!shared && !writable)

throw new NonWritableChannelException();

// 创建FileLock对象

FileLockImpl fli = new FileLockImpl(this, position, size, shared);

// 创建FileLockTable对象

FileLockTable flt = fileLockTable();

flt.add(fli);

boolean completed = false;

int ti = -1;

try {

// 标记开始IO操作 , 可能会导致阻塞

begin();

ti = threads.add();

if (!isOpen())

return null;

int n;

do {

// 开始锁住文件

n = nd.lock(fd, true, position, size, shared);

} while ((n == FileDispatcher.INTERRUPTED) && isOpen());

if (isOpen()) {

// 如果返回结果为RET_EX_LOCK的话

if (n == FileDispatcher.RET_EX_LOCK) {

assert shared;

FileLockImpl fli2 = new FileLockImpl(this, position, size,

false);

flt.replace(fli, fli2);

fli = fli2;

}

completed = true;

}

} finally {

// 释放锁

if (!completed)

flt.remove(fli);

threads.remove(ti);

try {

end(completed);

} catch (ClosedByInterruptException e) {

throw new FileLockInterruptionException();

}

}

return fli;

}

创建FileLockTable

private FileLockTable fileLockTable() throws IOException {

if (fileLockTable == null) {

synchronized (this) {

if (fileLockTable == null) {

// 判断系统属性sun.nio.ch.disableSystemWideOverlappingFileLockCheck

// 是否支持共享文件

if (isSharedFileLockTable()) {

int ti = threads.add();

try {

ensureOpen();

// 创建fileLockTable对象

fileLockTable = FileLockTable.newSharedFileLockTable(this, fd);

} finally {

threads.remove(ti);

}

} else {

fileLockTable = new SimpleFileLockTable();

}

}

}

}

return fileLockTable;

}

调用begin方法 , 设置中断触发

protected final void begin() {

if (interruptor == null) {

interruptor = new Interruptible() {

public void interrupt(Thread target) {

synchronized (closeLock) {

if (!open)

return;

open = false;

interrupted = target;

try {

AbstractInterruptibleChannel.this.implCloseChannel();

} catch (IOException x) { }

}

}};

}

blockedOn(interruptor);

Thread me = Thread.currentThread();

if (me.isInterrupted())

interruptor.interrupt(me);

}

在java.sun.nio.ch.FileDispatcherImpl.java中调用lock

int lock(FileDescriptor fd, boolean blocking, long pos, long size,

boolean shared) throws IOException

{

BlockGuard.getThreadPolicy().onWriteToDisk();

return lock0(fd, blocking, pos, size, shared);

}

在FileDispatcherImpl.c文件中

JNIEXPORT jint JNICALL

FileDispatcherImpl_lock0(JNIEnv *env, jobject this, jobject fdo,

jboolean block, jlong pos, jlong size,

jboolean shared)

{

// 通过fdval函数找到fd

jint fd = fdval(env, fdo);

jint lockResult = 0;

int cmd = 0;

// 创建flock对象

struct flock64 fl;

fl.l_whence = SEEK_SET;

// 从position位置开始

if (size == (jlong)java_lang_Long_MAX_VALUE) {

fl.l_len = (off64_t)0;

} else {

fl.l_len = (off64_t)size;

}

fl.l_start = (off64_t)pos;

// 如果是共享锁 , 则只读

if (shared == JNI_TRUE) {

fl.l_type = F_RDLCK;

} else {

// 否则可读写

fl.l_type = F_WRLCK;

}

// 设置锁参数

// F_SETLK : 给当前文件上锁(非阻塞)。

// F_SETLKW : 给当前文件上锁(阻塞,若当前文件正在被锁住,该函数一直阻塞)。

if (block == JNI_TRUE) {

cmd = F_SETLKW64;

} else {

cmd = F_SETLK64;

}

// 调用fcntl锁住文件

lockResult = fcntl(fd, cmd, &fl);

if (lockResult < 0) {

if ((cmd == F_SETLK64) && (errno == EAGAIN || errno == EACCES))

// 如果出现错误 , 返回错误码

return sun_nio_ch_FileDispatcherImpl_NO_LOCK;

if (errno == EINTR)

return sun_nio_ch_FileDispatcherImpl_INTERRUPTED;

JNU_ThrowIOExceptionWithLastError(env, "Lock failed");

}

return 0;

}

参考资料

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值