Pipe

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

Java NIO 中的 Pipe 和 FileLock 详解_文件锁

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​​ 是阻塞式的,如果未获取到文件锁,会一直阻塞当前线程,知道获取文件锁。

​trylock​​​ 和 ​​lock​​​ 的作用相同,只不过 ​​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();
}

}