java jni 文件 中断操作_Java NIO文件锁和可中断通道【源码笔记】

dedde186e6f7c49a40417faa9520912a.gif

目录

一、从一个例子开始

二、源码流程图

三、源码解读

1.图中第@5:标记I/O开始操作begin()源码

2.Thread的interrupt源码

3.图中第@6:标记I/O结束操作end()源码

四、Native lock0源码

五、总结

1.文件锁

2.可中断的通道

六、参考资料

七、系列文章

c314a3c760408f06a1db6eeda311dcfe.gif

dedde186e6f7c49a40417faa9520912a.gif

一、从一个例子开始

通过以下示例对源码进行跟踪。

File file = new File("/mytest/channletst.tmp");RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");FileChannel fileChannel = randomAccessFile.getChannel();FileLock fileLock = fileChannel.lock();

public final FileLock lock() throws IOException{return lock(0L, Long.MAX_VALUE, false); // @1}

备注:标记@1中lock()默认对整个文件进行加锁,默认使用独占锁

dedde186e6f7c49a40417faa9520912a.gif

二、源码流程图

deefe3d2486fc2cd59e9e36f6b223bd0.png

dedde186e6f7c49a40417faa9520912a.gif

三、源码解读

1.图中第@5:标记I/O开始操作begin()源码

protected final void begin() {if (interruptor == null) {// 实现Interruptible接口interruptor = new Interruptible() {// 具体实现的方法接受参数为Threadpublic void interrupt(Thread target) {synchronized (closeLock) {// 通道未开启直接返回if (!open)return;open = false;interrupted = target;try {// 释放锁关闭通道操作 AbstractInterruptibleChannel.this.implCloseChannel();} catch (IOException x) { }}}};}// 将interruptor赋值给当前线程Thread的成员变量Interruptible blocker// 以便线程中断时回调blockedOn(interruptor);Thread me = Thread.currentThread();// 线程被中断调用if (me.isInterrupted())interruptor.interrupt(me);}

2.Thread的interrupt源码

public void interrupt() {if (this != Thread.currentThread())checkAccess();synchronized (blockerLock) {// 注入的InterruptibleInterruptible b = blocker;if (b != null) {interrupt0();// 此处回调Interruptible#interrupt方法b.interrupt(this);return;}}interrupt0();}

小结:begin()操作即可中断线程的实现过程,在当前线程中注入Interruptible实例,当线程中断时对Interruptible进行回调;回调实现了关闭channel,释放锁操作。

3.图中第@6:标记I/O结束操作end()源码

protected final void end(boolean completed)throws AsynchronousCloseException{// 当前线程Thread的成员变量Interruptible blocker设置为nullblockedOn(null);Thread interrupted = this.interrupted;// 当前线程被中断抛出ClosedByInterruptExceptionif (interrupted != null && interrupted == Thread.currentThread()) {interrupted = null;throw new ClosedByInterruptException();}// I/O操作未完成而Channel通道被别的线程关闭// 抛出AsynchronousCloseExceptionif (!completed && !open)throw new AsynchronousCloseException();}

小结:end()操作标记I/O的结束,与begin()操作结对出现;根据不同的中断场景抛出不同的异常。

四、Native lock0源码

Java_sun_nio_ch_FileDispatcherImpl_lock0(JNIEnv *env, jobject this, jobject fdo,jboolean block, jlong pos, jlong size,jboolean shared){jint fd = fdval(env, fdo);jint lockResult = 0;int cmd = 0;struct flock64 fl;// l_start从文件头开始计算偏移值fl.l_whence = SEEK_SET;// 设置被加锁的长度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;}if (block == JNI_TRUE) {cmd = F_SETLKW64; // @1} else {cmd = F_SETLK64; // @2}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;}

flock64构造函数

struct flock64 {short l_type; // 锁类型short l_whence; // 决定l_start锁开始位置__kernel_loff_t l_start; // 锁定的位置__kernel_loff_t l_len; // 锁定的长度__kernel_pid_t l_pid; // 加锁的进程Pid__ARCH_FLOCK64_PAD};

备注:@1 F_SETLKW64

Sets or clears a file segment lock on a large file; however, if a shared or exclusive lock is blocked by other locks, fcntl() waits until the request can be satisfied. See File Locking for details. You must specify a third argument of type struct flock64 *. When you develop in C-based languages, it is necessary to compile the function with the _LARGE_FILE_API macro defined to use this symbol.

备注:@2 F_SETLK64

Sets or clears a file segment lock for a large file. You must specify a third argument of type struct flock64 *. See File Locking for details. fcntl() returns 0 if it successfully clears the lock. When you develop in C-based languages, it is necessary to compile the function with the _LARGE_FILE_API macro defined to use this symbol.

小结:F_SETLKW64与F_SETLK64作用相同;区别在于F_SETLKW64会阻塞。

dedde186e6f7c49a40417faa9520912a.gif

五、总结

1.文件锁

public final FileLock lock()public abstract FileLock lock(long position, long size, boolean shared)public abstract FileLock tryLock(long position, long size, boolean shared)public final FileLock tryLock()

总结:lock()与tryLock的区别在于,lock会阻塞调用Native API为F_SETLKW64;tryLock为非阻塞,调用Native API为F_SETLK64;锁与文件关联,而不是线程和通道,是进程级别的判优访问。

2.可中断的通道

总结:可中断的通道((Interruptible)实现InterruptibleChannel接口,可以被异步关闭(即另外线程调用该线程的的interrupt()方法);实现原理即文中的标记I/O开始的begin()和标记I/O结束的end()。

dedde186e6f7c49a40417faa9520912a.gif

六、参考资料

《Java NIO》

dedde186e6f7c49a40417faa9520912a.gif

七、系列文章

「瓜农老梁  学习同行」

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值