Java NIO之FileChannel 详解

关键点说明

  1. 文件打开选项

    • StandardOpenOption.CREATE - 文件不存在时创建

    • StandardOpenOption.READ/WRITE - 读写权限

    • StandardOpenOption.APPEND - 追加模式

    • StandardOpenOption.TRUNCATE_EXISTING - 清空已存在文件

  2. 缓冲区操作

    • ByteBuffer.wrap() 包装现有字节数组

    • buffer.flip() 切换读写模式

    • 直接操作缓冲区提高性能

  3. 高效文件复制

    • transferTo()/transferFrom() 方法比传统流复制更高效

  4. 资源管理

    • 使用try-with-resources确保通道自动关闭

    • 文件锁需要显式释放

这些示例展示了FileChannel的基本读写操作、文件锁的使用以及内存映射文件的高效操作,可以根据实际需求进行调整和扩展。

1. 基本文件读写示例

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;public class FileChannelDemo {public static void main(String[] args) {String filePath = "test.txt";// 写入文件writeToFile(filePath, "Hello, FileChannel!");// 读取文件String content = readFromFile(filePath);System.out.println("文件内容: " + content);// 追加内容appendToFile(filePath, "\n这是追加的内容");// 再次读取System.out.println("追加后的内容: " + readFromFile(filePath));// 文件复制copyFile(filePath, "test_copy.txt");System.out.println("复制文件内容: " + readFromFile("test_copy.txt"));}// 写入文件(覆盖)public static void writeToFile(String filePath, String content) {try (FileChannel channel = FileChannel.open(Paths.get(filePath),StandardOpenOption.CREATE,StandardOpenOption.WRITE,StandardOpenOption.TRUNCATE_EXISTING)) {ByteBuffer buffer = ByteBuffer.wrap(content.getBytes());channel.write(buffer);System.out.println("写入文件成功");} catch (IOException e) {e.printStackTrace();}}// 读取文件public static String readFromFile(String filePath) {try (FileChannel channel = FileChannel.open(Paths.get(filePath),StandardOpenOption.READ)) {ByteBuffer buffer = ByteBuffer.allocate((int) channel.size());channel.read(buffer);buffer.flip();return new String(buffer.array(), 0, buffer.limit());} catch (IOException e) {e.printStackTrace();return null;}}// 追加内容到文件public static void appendToFile(String filePath, String content) {try (FileChannel channel = FileChannel.open(Paths.get(filePath),StandardOpenOption.WRITE,StandardOpenOption.APPEND)) {ByteBuffer buffer = ByteBuffer.wrap(content.getBytes());channel.write(buffer);System.out.println("追加内容成功");} catch (IOException e) {e.printStackTrace();}}// 文件复制public static void copyFile(String sourcePath, String targetPath) {try (FileChannel source = FileChannel.open(Paths.get(sourcePath),StandardOpenOption.READ);FileChannel target = FileChannel.open(Paths.get(targetPath),StandardOpenOption.CREATE,StandardOpenOption.WRITE,StandardOpenOption.TRUNCATE_EXISTING)) {// 使用transferTo进行高效的文件复制source.transferTo(0, source.size(), target);System.out.println("文件复制成功");} catch (IOException e) {e.printStackTrace();}}
}

2. 使用文件锁的读写示例

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;public class FileChannelWithLockDemo {public static void main(String[] args) {String filePath = "locked_file.txt";// 线程1 - 写入数据new Thread(() -> {writeWithLock(filePath, "线程1写入的数据");}).start();// 线程2 - 尝试写入new Thread(() -> {writeWithLock(filePath, "线程2写入的数据");}).start();}public static void writeWithLock(String filePath, String content) {try (FileChannel channel = FileChannel.open(Paths.get(filePath),StandardOpenOption.CREATE,StandardOpenOption.WRITE,StandardOpenOption.APPEND)) {// 尝试获取排他锁System.out.println(Thread.currentThread().getName() + " 尝试获取文件锁...");FileLock lock = channel.lock();try {System.out.println(Thread.currentThread().getName() + " 获取到文件锁");// 模拟耗时操作Thread.sleep(2000);// 写入数据ByteBuffer buffer = ByteBuffer.wrap((content + "\n").getBytes());channel.write(buffer);System.out.println(Thread.currentThread().getName() + " 写入完成");} finally {lock.release();System.out.println(Thread.currentThread().getName() + " 释放文件锁");}} catch (IOException | InterruptedException e) {e.printStackTrace();}}
}

3.内存映射文件示例

import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;public class MemoryMappedFileDemo {public static void main(String[] args) {String filePath = "mapped_file.txt";// 写入内存映射文件writeMappedFile(filePath, "这是内存映射文件的内容");// 读取内存映射文件readMappedFile(filePath);}public static void writeMappedFile(String filePath, String content) {try (FileChannel channel = FileChannel.open(Paths.get(filePath),StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE)) {// 创建内存映射MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, content.getBytes().length);// 直接操作内存buffer.put(content.getBytes());System.out.println("内存映射文件写入完成");} catch (IOException e) {e.printStackTrace();}}public static void readMappedFile(String filePath) {try (FileChannel channel = FileChannel.open(Paths.get(filePath),StandardOpenOption.READ)) {MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());byte[] bytes = new byte[(int) channel.size()];buffer.get(bytes);System.out.println("读取到的内容: " + new String(bytes));} catch (IOException e) {e.printStackTrace();}}
}

FileChannel的lock详解

一、文件锁的基本概念

文件锁分为两种类型:

  1. 排他锁(独占锁)

    • 同一时间只能有一个进程持有

    • 其他进程无法获取任何类型的锁

  2. 共享锁(读锁)

    • 可以被多个进程同时持有

    • 但只要有进程持有共享锁,就不能获取排他锁

二、lock()方法的使用

1. 基本锁定方式

FileChannel channel = FileChannel.open(Paths.get("test.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE);// 获取排他锁(默认)
FileLock exclusiveLock = channel.lock();// 获取共享锁(第三个参数为true)
FileLock sharedLock = channel.lock(0, Long.MAX_VALUE, true);

2. 方法参数说明

public final FileLock lock(long position, long size, boolean shared)
  • position:锁定区域的起始位置

  • size:锁定区域的大小(比特)

  • shared:是否为共享锁(true=共享锁,false=排他锁)

3.非阻塞尝试锁定

FileLock tryLock = channel.tryLock(); // 非阻塞版本
if (tryLock == null) {// 获取锁失败
}
tryLock.release()

四、注意事项

  1. 锁的有效性

    • 文件锁是建议性的(advisory),不是强制性的

    • 只有遵守锁协议的进程才会受锁影响

    • 操作系统可能有不同的实现方式

  2. 性能考虑

    • 频繁获取/释放锁会影响性能

    • 锁定大文件区域可能降低并发性

  3. 异常处理

    • OverlappingFileLockException:当请求的锁区域与现有锁重叠时抛出

    • NonWritableChannelException:尝试在只读通道上获取排他锁

  4. 平台差异

    • Windows系统上的实现与Unix-like系统不同

    • 某些网络文件系统可能不支持文件锁

五、适用场景

  1. 多进程共享文件访问控制

  2. 防止文件被多个写入者同时修改

  3. 实现简单的进程间同步机制

  4. 保护关键配置文件不被并发修改

正确使用FileChannel的锁机制可以有效地管理对共享文件的并发访问,保证数据的一致性和完整性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/74917.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

stock-pandas,一个易用的talib的替代开源库。

原创内容第841篇,专注智能量化投资、个人成长与财富自由。 介绍一个ta-lib的平替——我们来实现一下,最高价突破布林带上轨,和最低价突破布林带下轨的可视化效果: cross_up_upper stock[high].copy()# cross_up_upper 最高价突破…

JVM 面经

1、什么是 JVM? JVM 就是 Java 虚拟机,它是 Java 实现跨平台的基石。程序运行之前,需要先通过编译器将 Java 源代码文件编译成 Java 字节码文件;程序运行时,JVM 会对字节码文件进行逐行解释,翻译成机器码指令&#x…

【JavaScript】合体期功法——DOM(一)

目录 DOMWeb API 基本概念作用和分类 什么是 DOMDOM 树DOM 对象 获取 DOM 元素根据 CSS 选择器来获取 DOM 元素选择匹配的第一个元素选择匹配的多个元素 其他获取 DOM 元素方法 修改元素的内容对象.innerText 属性对象.innerHTML 属性案例:年会抽奖 修改元素属性修改…

GAMMA数据处理(十)

今天向别人请教了一个问题,刚无意中搜索到了一模一样的问题 不知道这个怎么解决... ok 解决了 有一个GAMMA的命令可转换 但是很奇怪 完全对不上 转换出来的行列号 不知道为啥 再试试 是因为经纬度坐标的小数点位数 de as

Java入门知识总结——章节(二)

ps:本章主要讲数组、二维数组、变量 一、数组 数组是一个数据容器,可用来存储一批同类型的数据 🔑:注意 类也可以是一个类的数组 public class Main {public static class Student {String name;int age; // 移除 unsignedint…

动态IP:网络世界的“变色龙”如何改变你的在线体验?

你知道吗?有时候我觉得动态IP就像是网络世界里的“变色龙”。它不像静态IP那样一成不变,而是随时在变化,像是一个永远在换衣服的演员。你永远不知道它下一秒会变成什么样子,但正是这种不确定性,让它变得特别有趣。想象…

从24GHz到71GHz:Sivers半导体的广泛频率范围5G毫米波产品解析

在5G技术的浪潮中,Sivers半导体推出了创新的毫米波无线产品,为通信行业带来高效、可靠的解决方案。这些产品支持从24GHz到71GHz的频率,覆盖许可与非许可频段,适应高速、低延迟的通信场景。 5G通信频段的一点事儿及Sivers毫米波射频…

aocache:AOCache 新增功能深度解析:从性能监控到灵活配置的全方位升级

最近对aocache 进行了重要升级,最新版本0.6.0增加了几项新功能:性能分析日志,AOCache性能分析工具,切入点自定义配置,全局配置,本文详细说明这几项目新功能的作用和使用方式。 一、性能分析日志 需求背景…

Java EE 进阶:MyBatis-plus

MyBatis-plus的介绍 MyBatis-plus是MyBatis的增强工具,在MyBatis的基础上做出加强,只要MyBatis有的功能MyBatis-plus都有。 MyBatis-plus的上手 添加依赖 在我们创建项目的时候,我们需要添加MyBatis-plus和mysql的依赖 MyBatis-plus的依赖…

GitHub和Gitee上的一些AI项目

以下是GitHub和Gitee上的一些AI项目: GitHub上的AI项目 TensorFlow:一个端到端开源机器学习平台,包含大量工具和库,广泛应用于图像识别、自然语言处理等领域。PyTorch:由Facebook开发的开源深度学习框架,…

JavaScript网页设计高级案例:构建交互式图片画廊

JavaScript网页设计高级案例:构建交互式图片画廊 在现代Web开发中,交互式元素已成为提升用户体验的关键因素。本文将通过一个高级案例 - 构建交互式图片画廊,展示如何结合HTML和JavaScript创建引人入胜的网页应用。这个案例不仅涵盖了基础的…

Linux命令大全:从入门到高效运维

适合人群:Linux新手 | 运维工程师 | 开发者 目录 一、Linux常用命令(每天必用) 1. 文件与目录操作 2. 文件内容查看与编辑 二、次常用命令(按需使用) 1. 系统管理与监控 2. 网络与通信 3. 权限与用户管理 三、…

Windows 10/11 使用 VSCode + SSH 免密远程连接 Ubuntu 服务器(指定端口)

摘要: 本文详细介绍如何在 Windows 系统上通过 VSCode Remote-SSH 免密登录远程 Ubuntu 服务器(SSH 端口 2202),避免每次输入密码的繁琐操作,提高开发效率。 1. 环境准备 本地系统:Windows 10/11远程服务…

一些需要学习的C++库:CGAL和Eysshot

写在前面: 从开始工作到现在,去过多家公司,多个行业, 虽然大部分时间在通信业,但也有其它的行业的工作没有做完,但也很感兴趣。每次想要研究一下时,总是想不起来。 这里写一些信息,…

蓝桥杯16天刷题计划一一Day01

蓝桥杯16天刷题计划一一Day01(STL练习) 作者:blue 时间:2025.3.26 文章目录 蓝桥杯16天刷题计划一一Day01(STL练习)[P1540 [NOIP 2010 提高组\] 机器翻译 - 洛谷 (luogu.com.cn)](https://www.luogu.com.…

相对位置2d矩阵和kron运算的思考

文章目录 1. 相对位置矩阵2d2. kron运算 1. 相对位置矩阵2d 在swin-transformer中,我们会计算每个patch之间的相对位置,那么我们看到有一连串的拉伸和相减,直接贴代码: import torch import torch.nn as nntorch.set_printoptio…

Redis 版本演进及主要新特性

Redis 版本发布历史 稳定版本时间线 Redis 2.6 (2012年)Redis 2.8 (2013年11月)Redis 3.0 (2015年4月) - 首次支持集群Redis 3.2 (2016年5月)Redis 4.0 (2017年7月)Redis 5.0 (2018年10月)Redis 6.0 (2020年4月)Redis 6.2 (2021年2月)Redis 7.0 (2022年4月) - 最新稳定版(截至…

HTML5 Geolocation(地理定位)学习笔记

一、HTML5 Geolocation简介 HTML5 Geolocation(地理定位)API用于获取用户的地理位置信息。通过这个API,可以获取用户的纬度、经度、海拔等信息。由于地理定位可能涉及用户隐私,因此只有在用户同意的情况下,才能获取其…

爱普生VG3225EFN压控晶振5G基站低噪声的解决方案

在 5G 通信网络的高速发展中,系统噪声的控制成为保障网络可靠性与数据吞吐量的关键。爱普生 VG3225EFN 压控晶振凭借其卓越的低噪声特性,成为 5G 基站时钟系统的理想选择。通过创新的技术设计,这款晶振不仅为基站提供了稳定的时钟基准&#x…

【问题解决】Linux安装conda修改~/.bashrc配置文件后,root 用户下显示 -bash-4.2#

问题描述 在Linux安装conda下的python环境时候,修改了~/.bashrc文件,修改完成后,再次进入服务器后,登录时候显示的不是正常的[rootlocalhost ~]#,而是-bash-4.2# 原因分析: 网上原因有:/root下…