Java NIO 中的 Pipe 和 FileLock 详解

简介: Java NIO 中的 Pipe 和 FileLock 详解

Pipe


Java NIO 管道是 2 个线程之间的单向数据连接, Pipe 有一个 source 通道和一个 sink 通道。数据会被下入到 Sink 通道,从 source 通道读取。


image.png


1、创建通道


通过 Pipe.open() 方法打开管道


Pipe pipe = Pipe.open();


2、写入通道


要向管道写入数据,需要访问 sink 通道。


Pipe.Sinkchannel sinkChannel = pipe.sink();


通过调用 Sinkchannel 的 write() 方法,将数据写入 SinkChannel :


String newData = "New String to write to file .." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.alloacte(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()) {
  sinkChannel.write(buf);
}


3、从管道读取数据


从读取管道的数据,需要访问 source 通道,像这样:


Pipe.SourceChannel sourceChannel = pipe.source();


调用 source 通道的 read() 方法来读取数据:


ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = sourceChannel.read(buf);


read() 方法返回的 int 值会告诉我们多少字节被读进了缓冲区。


4、示例


我们将通过Pipe,Sink进行数据的读写操作, 示例如下:


public class PipeDemo {
    public static void main(String[] args) throws IOException {
        // 1 获取管道
        Pipe pipe = Pipe.open();
        // 2 获取 sink 通道
        Pipe.SinkChannel sinkChannel = pipe.sink();
        // 3 创建缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        buffer.put("ha ha!".getBytes(StandardCharsets.UTF_8));
        buffer.flip();
        // 4 写入数据
        sinkChannel.write(buffer);
        // 5 source 通道
        Pipe.SourceChannel sourceChannel = pipe.source();
        // 6 创建一个缓冲区,读取数据
        ByteBuffer buffer2 = ByteBuffer.allocate(1024);
        //buffer2.flip();
        int len = sourceChannel.read(buffer2);
        String str = new String(buffer2.array(), 0, len);
        System.out.println(str);
        // 7 关闭通道
        sinkChannel.close();
        sourceChannel.close();
    }
}


FileLock


FileLock 简介


文件锁在 OS 中很常见,如果多个程序同时访问、修改同一个文件,很容易英文文件数据不同步而出现问题。给文件加一个锁,同一事件,只能有一个程序修改此文件,或者程序都只能读此文件,这就解决了同步问题。


文件锁是进程级别的,不是线程级别的,文件锁可以解决多个线程并发的访问、修改同一个文件的问题,但是不能解决多线程并发访问,修改同一个文件的问题。


使用文件锁时,同一个进程内的多个线程,可以同时访问,修改此文件。


文件锁是当前程序锁属的 JVM 实例持有的,一旦获取到文件锁(对文件加锁),要调用 release() 或者关闭对应 FileChannel 对象,或者当前 JVM 退出才会释放这个锁。


一旦某个进程(比如 JVM 实例)对某个文件加锁,则在释放这个锁之前,此进程不不能在对此文件加锁,就是 JVM 实例在同一个问加上的文件锁是不能重叠的(进程级别不能重复在同一文件上获取锁)。


2、文件锁分配


排他锁: 又叫独占锁,对文件加排他锁后,该进程可以对此文件进行读写,该进程独占此文件,其他进程不能读写此文件,知道该文件释放文件锁。


共享锁: 某个进程对文件加共享锁,其他进程也可以访问此文件,但是这些进程都只能读此文件,不能写。线程是安全的。只要还有一个进程持有共享锁,此文件只能读,不能写。(其实只能共享读,不能共享写)。


3、使用示例


// 创建 FileChannel 对象,文件锁只能通过 FileChannel 对象来使用
String pathName = "/xxx/01.txt";
FileChannel fileChannel = new FileOutputStream(pathName).getChannel();
// 对文件加锁
FileLock lock = fileChannel.lock();
// 对此文件进行读写操作
// ....
// 释放锁
lock.release();


文件锁要通过 FileChannel 对象使用。


4、获取文件锁方法


有 4 中获取文件锁的方法:


  • lock() // 对整个文件加锁,默认为排他锁


  • lock(long position, long size, boolean shared) // 自定义加锁方式。前面两个参数指定要加锁的部分(可以只对此文件的部分内容加锁),第三个参数值指定是否共享锁。


  • trylock() // 对整个文件加锁,默认为排他锁。


  • trylock(long position, long size, boolean shared) // 自定义加锁方式。如果指定为共享锁,则其他进程可以读此文件,所有进程均不能写此文件,如果某进程视图对此文件进行写操作,会抛出异常。


5、lock 与 trylock 区别


lock 是阻塞式的,如果未获取到文件锁,会一直阻塞当前线程,知道获取文件锁。

trylocklock 的作用相同,只不过 trylock 是非阻塞式的,trylock 是尝试获取文件锁,获取成功后就返回锁对象,否则返回 null, 不会阻塞当前线程。


6、fileLock 两个方法


boolean isShared() // 此文件锁是否共享锁


boolean isValid() // 此文件锁是否还有效果


在某些 OS 上,对某个文件加锁后,不能对此文件使用通道映射。


7、完整示例


public class FileLockDemo2 {
    public static void main(String[] args) throws IOException {
        String input = "hello!";
        System.out.println(input);
        ByteBuffer buffer = ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8));
        String pathName = "/xxx/01.txt";
        Path path = Paths.get(pathName);
        FileChannel fileChannel = FileChannel.open(path,
                                                   StandardOpenOption.WRITE, StandardOpenOption.APPEND);
        fileChannel.position(fileChannel.size() - 1);
        // 加锁
        FileLock lock = fileChannel.lock();
    // 如果是共享锁则不能执行后续的 write 操作
        //FileLock lock = fileChannel.lock(0, Long.MAX_VALUE, true);
        System.out.println("isShared: " + lock.isShared());
        fileChannel.write(buffer);
        fileChannel.close();
        readFile(pathName);
    }
    private static void readFile(String pathName) throws IOException {
        FileReader fileReader = new FileReader(pathName);
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        String str = bufferedReader.readLine();
        System.out.println("读取内容:");
        while (str != null) {
            System.out.println("  " + str);
            str = bufferedReader.readLine();
        }
        fileReader.close();
        bufferedReader.close();
    }


相关文章
|
17天前
|
监控 Java API
探索Java NIO:究竟在哪些领域能大显身手?揭秘原理、应用场景与官方示例代码
Java NIO(New IO)自Java SE 1.4引入,提供比传统IO更高效、灵活的操作,支持非阻塞IO和选择器特性,适用于高并发、高吞吐量场景。NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器(Selector),能实现多路复用和异步操作。其应用场景涵盖网络通信、文件操作、进程间通信及数据库操作等。NIO的优势在于提高并发性和性能,简化编程;但学习成本较高,且与传统IO存在不兼容性。尽管如此,NIO在构建高性能框架如Netty、Mina和Jetty中仍广泛应用。
28 3
|
25天前
|
存储 监控 Java
Java的NIO体系
通过本文的介绍,希望您能够深入理解Java NIO体系的核心组件、工作原理及其在高性能应用中的实际应用,并能够在实际开发中灵活运用这些知识,构建高效的Java应用程序。
32 5
|
2月前
|
消息中间件 缓存 Java
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
零拷贝技术 Zero-Copy 是指计算机执行操作时,可以直接从源(如文件或网络套接字)将数据传输到目标缓冲区, 而不需要 CPU 先将数据从某处内存复制到另一个特定区域,从而减少上下文切换以及 CPU 的拷贝时间。
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
|
2月前
|
Java
如何在 Java 中处理“Broken Pipe”异常
在Java中处理“Broken Pipe”异常,通常发生在网络通信中,如Socket编程时。该异常表示写入操作的另一端已关闭连接。解决方法包括:检查网络连接、设置超时、使用try-catch捕获异常并进行重试或关闭资源。
127 5
|
2月前
|
数据采集 Java Linux
Java“Broken Pipe”解决
Java中遇到“Broken Pipe”错误通常是因为Socket连接被远程主机关闭,而本地程序仍在尝试写入数据。解决方法包括:1. 检查网络连接和防火墙设置;2. 增加超时设置;3. 使用异常处理捕获并重试。
483 4
|
4月前
|
存储 网络协议 Java
Java NIO 开发
本文介绍了Java NIO(New IO)及其主要组件,包括Channel、Buffer和Selector,并对比了NIO与传统IO的优势。文章详细讲解了FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel及Pipe.SinkChannel和Pipe.SourceChannel等Channel实现类,并提供了示例代码。通过这些示例,读者可以了解如何使用不同类型的通道进行数据读写操作。
Java NIO 开发
|
3月前
|
Java
让星星⭐月亮告诉你,Java NIO之Buffer详解 属性capacity/position/limit/mark 方法put(X)/get()/flip()/compact()/clear()
这段代码演示了Java NIO中`ByteBuffer`的基本操作,包括分配、写入、翻转、读取、压缩和清空缓冲区。通过示例展示了`position`、`limit`和`mark`属性的变化过程,帮助理解缓冲区的工作原理。
42 2
|
5月前
|
Java
"揭秘Java IO三大模式:BIO、NIO、AIO背后的秘密!为何AIO成为高并发时代的宠儿,你的选择对了吗?"
【8月更文挑战第19天】在Java的IO编程中,BIO、NIO与AIO代表了三种不同的IO处理机制。BIO采用同步阻塞模型,每个连接需单独线程处理,适用于连接少且稳定的场景。NIO引入了非阻塞性质,利用Channel、Buffer与Selector实现多路复用,提升了效率与吞吐量。AIO则是真正的异步IO,在JDK 7中引入,通过回调或Future机制在IO操作完成后通知应用,适合高并发场景。选择合适的模型对构建高效网络应用至关重要。
107 2
|
5月前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
170 0
|
6月前
|
安全 Java Linux
(七)Java网络编程-IO模型篇之从BIO、NIO、AIO到内核select、epoll剖析!
IO(Input/Output)方面的基本知识,相信大家都不陌生,毕竟这也是在学习编程基础时就已经接触过的内容,但最初的IO教学大多数是停留在最基本的BIO,而并未对于NIO、AIO、多路复用等的高级内容进行详细讲述,但这些却是大部分高性能技术的底层核心,因此本文则准备围绕着IO知识进行展开。
201 1