Pipe
Java NIO 管道是 2 个线程之间的单向数据连接, Pipe 有一个 source 通道和一个 sink 通道。数据会被下入到 Sink 通道,从 source 通道读取。
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();
}
}