Java IO教程 - Java目录事件
当文件系统中的对象被修改时,我们可以监听watch服务以获取警报。
java.nio.file包中的以下类和接口提供watch服务。
- Watchable接口
- WatchService接口
- WatchKey接口
- WatchEvent接口
- WatchEvent.Kind接口
- StandardWatchEventKinds类
可监视对象表示可以被监视的文件系统对象。可观看对象可以向手表服务注册。
Path对象是一个Watchable对象。
WatchService表示观察服务。当一个对象使用WatchService注册时,WatchService返回一个WatchKey作为注册的令牌。
WatchEvent表示注册到监视服务的对象上的事件。它的kind()方法返回发生的事件的类型。
它的context()方法返回一个Path对象,它表示事件发生的条目。
count()方法返回特定通知的事件发生次数。 如果它返回大于1的值,那么它是一个重复的事件。
WatchEvent.Kind <T>表示发生的事件的类型。
StandardWatchEventKinds类定义了用于表示事件种类的常量,如下所示。
- ENTRY_CREATE
- ENTRY_DELETE
- ENTRY_MODIFY
- OVERFLOW
OVERFLOW表示丢失或丢弃的事件。
创建观察服务以观察目录以进行更改。
WatchService ws = FileSystems.getDefault().newWatchService();
要使用Watch服务注册目录,使用register()方法,该方法将返回一个WatchKey对象作为注册令牌。
// Get a Path object for C:\myName directory to watch Path dirToWatch = Paths.get("C:\\myName"); WatchKey token = dirToWatch.register(ws, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
要取消注册,请使用WatchKey的cancel()方法。
当注册目录时,其WatchKey处于就绪状态。
我们可以通过手表服务注册多个目录。
要从监视服务队列中检索WatchKey,使用WatchService对象的take()或poll()方法来检索和删除发出信号并排队的WatchKey。
take()方法等待,直到WatchKey可用。poll()方法允许我们为等待指定超时。
以下代码使用无限循环来检索发出信号的WatchKey。
while(true) {WatchKey key = ws.take(); }
处理事件
WatchKey的pollEvents()方法检索并删除所有挂起的事件。它返回一个WatchEvent的列表。 List的每个元素代表WatchKey上的一个事件。
以下代码显示了处理事件的典型逻辑:
while(true) {WatchKey key = ws.take();// Process all events of the WatchKey for(WatchEvent<?> event : key.pollEvents()) {// Process each event here} }
处理事件后重置WatchKey
我们需要重置WatchKey对象,通过调用其reset()方法来再次接收事件通知。
reset()方法将WatchKey置于就绪状态。如果WatchKey仍然有效,reset()方法返回true。 否则,它返回false。
如果WatchKey被取消或其监视服务关闭,它可能会失效。
// Reset the WatchKey boolean isKeyValid = key.reset(); if (!isKeyValid) {System.out.println("No longer watching " + dirToWatch); }
WatchService是可自动关闭的。我们可以在try-with-resources中创建一个WatchService的对象块,当程序退出块时它将自动关闭。
例子
以下代码显示了如何实现监视服务以监视目录中的更改。
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; import static java.nio.file.StandardWatchEventKinds.OVERFLOW;import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.WatchEvent; import java.nio.file.WatchEvent.Kind; import java.nio.file.WatchKey; import java.nio.file.WatchService;public class Main {public static void main(String[] args) {try (WatchService ws = FileSystems.getDefault().newWatchService()) {Path dirToWatch = Paths.get("C:\\myName");dirToWatch.register(ws, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);while (true) {WatchKey key = ws.take();for (WatchEvent<?> event : key.pollEvents()) {Kind<?> eventKind = event.kind();if (eventKind == OVERFLOW) {System.out.println("Event overflow occurred");continue;}WatchEvent<Path> currEvent = (WatchEvent<Path>) event;Path dirEntry = currEvent.context();System.out.println(eventKind + " occurred on " + dirEntry);}boolean isKeyValid = key.reset();if (!isKeyValid) {System.out.println("No longer watching " + dirToWatch);break;}}} catch (IOException | InterruptedException e) {e.printStackTrace();}} }
Java IO教程 - Java异步I/O
在同步文件I/O中,对I/O操作的请求将等待,直到I/O操作完成。
在异步文件I/O中,I/O操作的请求由系统异步执行。
当系统完成文件I/O时,它通知应用程序其请求的完成。
java.nio.channels.AsynchronousFileChannel类表示异步文件通道。
AsynchronousFileChannel类的静态open()方法获取AsynchronousFileChannel类的实例。
以下代码显示了如何获取WRITE的异步文件通道。
Path path = Paths.get("C:\\Java_Dev\\rainbow.txt"); AsynchronousFileChannel afc = AsynchronousFileChannel.open(path, WRITE, CREATE);
AsynchronousFileChannel提供了两种方法来处理异步文件I/O操作的结果。
- Using a java.util.concurrent.Future object.
- Using a java.nio.channels.CompletionHandler object.
支持异步文件I/O操作的AsynchronousFileChannel类的每个方法有两个版本。
一个版本返回一个Future对象,我们可以使用它来处理所请求的异步操作的结果。
Future对象的get()方法返回写入文件通道的字节数。
以下代码使用返回Future对象的write()方法的版本:
ByteBuffer dataBuffer = a buffer; long startPosition = 0; Future<Integer> result = afc.write(dataBuffer, startPosition);
一旦我们得到一个Future对象,我们可以使用轮询方法或阻塞等待方法来处理异步文件I/O的结果。
下面的代码显示了轮询方法,它将继续调用Future对象的isDone()方法,以检查I/O操作是否完成:
while (!result.isDone()) { } int writtenNumberOfBytes = result.get();
AsynchronousFileChannel类的另一个版本的方法获得一个CompletionHandler对象,当请求的异步I/O操作完成或失败时,该对象的方法被调用。
CompletionHandler接口有两个方法:completed()和failed()。
当所请求的I/O操作成功完成时,将调用completed()方法。
当请求的I/O操作时失败,则调用failed()方法。
以下代码使用Attachment类的对象作为完成处理程序的附件:
class Attachment {public Path path;public ByteBuffer buffer;public AsynchronousFileChannel asyncChannel; } class MyHandler implements CompletionHandler<Integer, Attachment> {@Overridepublic void completed(Integer result, Attachment attach) {// Handle completion of the I/O operation}@Overridepublic void failed(Throwable e, Attachment attach) {// Handle failure of the I/O operation} }
以下代码使用MyHandler实例作为异步写操作的完成处理程序。
MyHandler handler = new MyHandler(); ByteBuffer dataBuffer = get a data buffer; Attachment attach = new Attachment(); attach.asyncChannel = afc; attach.buffer = dataBuffer; attach.path = path;// Perform the asynchronous write operation afc.write(dataBuffer, 0, attach, handler);
以下代码演示了如何使用CompletionHandler对象来处理对文件的异步写入的结果。
import static java.nio.file.StandardOpenOption.CREATE; import static java.nio.file.StandardOpenOption.WRITE;import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousFileChannel; import java.nio.channels.CompletionHandler; import java.nio.charset.Charset; import java.nio.file.Path; import java.nio.file.Paths; public class Main {public static void main(String[] args) throws Exception {Path path = Paths.get("test.txt");AsynchronousFileChannel afc = AsynchronousFileChannel.open(path, WRITE,CREATE);WriteHandler handler = new WriteHandler();ByteBuffer dataBuffer = getDataBuffer();Attachment attach = new Attachment();attach.asyncChannel = afc;attach.buffer = dataBuffer;attach.path = path;afc.write(dataBuffer, 0, attach, handler);System.out.println("Sleeping for 5 seconds...");Thread.sleep(5000);}public static ByteBuffer getDataBuffer() {String lineSeparator = System.getProperty("line.separator");StringBuilder sb = new StringBuilder();sb.append("test");sb.append(lineSeparator);sb.append("test");sb.append(lineSeparator);String str = sb.toString();Charset cs = Charset.forName("UTF-8");ByteBuffer bb = ByteBuffer.wrap(str.getBytes(cs));return bb;} } class Attachment {public Path path;public ByteBuffer buffer;public AsynchronousFileChannel asyncChannel; }class WriteHandler implements CompletionHandler<Integer, Attachment> {@Overridepublic void completed(Integer result, Attachment attach) {System.out.format("%s bytes written to %s%n", result,attach.path.toAbsolutePath());try {attach.asyncChannel.close();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void failed(Throwable e, Attachment attach) {try {attach.asyncChannel.close();} catch (IOException e1) {e1.printStackTrace();}} }
例子
以下代码演示了如何使用Future对象来处理对文件的异步写入的结果。
import static java.nio.file.StandardOpenOption.CREATE; import static java.nio.file.StandardOpenOption.WRITE;import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousFileChannel; import java.nio.charset.Charset; import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.Future;public class Main {public static ByteBuffer getDataBuffer() {String lineSeparator = System.getProperty("line.separator");StringBuilder sb = new StringBuilder();sb.append("test");sb.append(lineSeparator);String str = sb.toString();Charset cs = Charset.forName("UTF-8");ByteBuffer bb = ByteBuffer.wrap(str.getBytes(cs));return bb;}public static void main(String[] args) throws Exception {Path path = Paths.get("test.txt");try (AsynchronousFileChannel afc = AsynchronousFileChannel.open(path,WRITE, CREATE)) {ByteBuffer dataBuffer = getDataBuffer();Future<Integer> result = afc.write(dataBuffer, 0);while (!result.isDone()) {System.out.println("Sleeping for 2 seconds...");Thread.sleep(2000);}int writtenBytes = result.get();System.out.format("%s bytes written to %s%n", writtenBytes,path.toAbsolutePath());} catch (IOException e) {e.printStackTrace();}} }
上面的代码生成以下结果。
例2
以下代码演示了如何使用CompletionHandler对象来处理从文件进行异步读取的结果。
import static java.nio.file.StandardOpenOption.READ;import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousFileChannel; import java.nio.channels.CompletionHandler; import java.nio.charset.Charset; import java.nio.file.Path; import java.nio.file.Paths; public class Main {public static void main(String[] args) throws Exception{Path path = Paths.get("test.txt");AsynchronousFileChannel afc = AsynchronousFileChannel.open(path, READ);ReadHandler handler = new ReadHandler();int fileSize = (int) afc.size();ByteBuffer dataBuffer = ByteBuffer.allocate(fileSize);Attachment attach = new Attachment();attach.asyncChannel = afc;attach.buffer = dataBuffer;attach.path = path;afc.read(dataBuffer, 0, attach, handler);System.out.println("Sleeping for 5 seconds...");Thread.sleep(5000);} } class Attachment {public Path path;public ByteBuffer buffer;public AsynchronousFileChannel asyncChannel; }class ReadHandler implements CompletionHandler<Integer, Attachment> {@Overridepublic void completed(Integer result, Attachment attach) {System.out.format("%s bytes read from %s%n", result, attach.path);System.out.format("Read data is:%n");byte[] byteData = attach.buffer.array();Charset cs = Charset.forName("UTF-8");String data = new String(byteData, cs);System.out.println(data);try {// Close the channelattach.asyncChannel.close();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void failed(Throwable e, Attachment attach) {System.out.format("Read operation on %s file failed."+ "The error is: %s%n", attach.path, e.getMessage());try {// Close the channelattach.asyncChannel.close();} catch (IOException e1) {e1.printStackTrace();}} }
上面的代码生成以下结果。
例3
以下代码显示了如何使用Future对象来处理从文件进行异步读取的结果。它使用等待方法(Future.get()方法调用)等待异步文件I/O完成。
import static java.nio.file.StandardOpenOption.READ;import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousFileChannel; import java.nio.charset.Charset; import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future;public class Main {public static void main(String[] args) throws Exception {Path path = Paths.get("test.txt");try (AsynchronousFileChannel afc = AsynchronousFileChannel.open(path, READ)) {int fileSize = (int) afc.size();ByteBuffer dataBuffer = ByteBuffer.allocate(fileSize);Future<Integer> result = afc.read(dataBuffer, 0);int readBytes = result.get();System.out.format("%s bytes read from %s%n", readBytes, path);System.out.format("Read data is:%n");byte[] byteData = dataBuffer.array();Charset cs = Charset.forName("UTF-8");String data = new String(byteData, cs);System.out.println(data);} catch (IOException ex) {ex.printStackTrace();}} }
上面的代码生成以下结果。