跨进程文件锁 - FileChannel
时间:2022-07-25
本文章向大家介绍跨进程文件锁 - FileChannel,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
背景
当有多个进程或者多个应用同时操作文件时 , 会并行往文件中写入字节 , 如何保证多个进程中文件写入或者操作当原子性就很重要.
此时 , 在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;
}
参考资料
- 零基础学编程006:赋值语句
- 零基础学编程005:打印一行复利数据
- 使用Keras集成卷积神经网络的入门级教程
- 零基础学编程003:在游戏中学JAVA和C#
- 在机器学习中处理缺失数据的方法
- 零基础学编程002:Hello World
- php随机数怎么获取?一个简单的函数就能生成
- 零基础学编程009:只显示2位小数
- .Net实现Windows服务安装完成后自动启动的两种方法
- 【教程】OpenCV—Node.js教程系列:Node.js+OpenCV面部脸识别
- 学会10多种语言是种什么样的体验?
- Spring Data 最佳实践
- Prodigy,从根本上有效的自主学习驱动的注释工具
- 零基础学编程017:画出我的公众号LOGO
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Elasticsearch: Rare Terms Aggregation
- Elasticsearch: 运用 Pinned query 来提高文档的排名 (7.5发行版新功能)
- leetcode树之从翻转二叉树
- DiskDB,一个不用网的数据库
- 何时用多线程?多线程需要加锁吗?线程数多少最合理?
- Spring Cloud @RefreshScope 刷新机制必知道
- “非主流”的纯前端性能优化
- CICD(二) Ansible
- JS Sequence Diagrams
- kubernetes(二十一) 微服务链路监控& 自动发布
- kubernetes(八) kubernetes的使用
- kubernetes(九) kubernetes控制器
- kubernetes(十) kubernetes service,ingress&cm,secret
- kubernetes(十一) 存储& statefulset控制器
- kubernetes(十二) 准入控制和helm v3包管理