NIO2

NIO2是在NIO的基础上进行的扩展。Java 7 对原有的NIO进行了改进:

  • 提供了全面的文件IO和文件系统访问支持:表现为Java 7新增的java.nio.file包及其各个子包
  • 基于异步Channel的IO : 在java.nio.channels包下新增了多个以Asynchronous开头的Channel接口和类

Java 8进一步增强了Files工具类的用法,允许开发者使用Stream API来操作文件目录和文件内容。

 

一、重要类

NIO2或者说NIO中的核心类:Path、 Paths、Files

对应早期Java中的File类,File类是用来访问文件系统,但是功能有限(尽管我还用的挺舒服的),它不能利用特定文件系统的特性,其提供方法的访问性能也不高,NIO2为了弥补这种不足,引入了Path接口,Path代表一个平台无关的平台路径。

另外Files、Paths是两个工具类,包含大量的静态方法。

1、Path类

 对应原有的java.io.File类

 

使用示例:

import java.nio.file.Path;

import java.nio.file.Paths;

 

public class PathTest {

    public static void main(String[] args) {

        Path path = Paths.get(".");

        System.out.println("path路径:" + path);  // 输出: path路径:.

        System.out.println("path里包含的路径数量:" + path.getNameCount());  // 结果为1

        System.out.println("path的根路径:" + path.getRoot()); // 结果为null

        System.out.println("path的父路径:" + path.getParent()); // 输出: path的父路径:null

 

        Path absolutePath = path.toAbsolutePath();

        System.out.println("path的绝对路径:" + absolutePath); // 输出: path的绝对路径:/Users/qinwenjing/Documents/Projects/Test/.

        System.out.println("path的绝对路径名称:" + absolutePath.getFileName()); // 输出: path的绝对路径名称:.

        System.out.println("path的绝对路径的根路径:" + absolutePath.getRoot()); // 输出: path的绝对路径的根路径:/

        System.out.println("path的绝对路径数量:" + absolutePath.getNameCount());

        System.out.println("path的绝对路径下标为3的路径名称:" + absolutePath.getName(3));

        System.out.println("path的绝对路径的父路径:" + absolutePath.getParent()); // path的绝对路径的父路径:/Users/qinwenjing/Documents/Projects/Test

 

        // 以多个string构造Path

        Path path1 = Paths.get("g:""publish""codes");

        System.out.println(path1);

    }

}

2、Files工具类、Paths工具类

import java.io.FileOutputStream;

import java.io.IOException;

import java.nio.charset.Charset;

import java.nio.file.FileStore;

import java.nio.file.Files;

import java.nio.file.Path;

import java.nio.file.Paths;

import java.util.List;

 

public class FilesTest {

    public static void main(String[] args) {

        try {

            Path path = Paths.get("/Users/qinwenjing/Documents/Projects/Test/test2.txt");

            // 复制文件

            Files.copy(path, new FileOutputStream("b.txt"));

            // 判断是否是隐藏文件

            System.out.println(Files.isHidden(path));

            // 输出文件的大小

            System.out.println(Files.size(path));

 

            // 一次性读取文件中所有的行

            List<String> linesList = Files.readAllLines(path);

            System.out.println(linesList);

 

            // 直接将多个字符串写入到指定的文件

            Files.write(Paths.get("/Users/qinwenjing/Documents/Projects/Test/d.txt"), linesList, Charset.forName

                    ("utf-8"));

 

            // 列出当前目录下所有的文件和子目录

            Files.list(Paths.get(".")).forEach(p -> System.out.println(p));

 

            System.out.println("--------读取文件内容---------");

            Files.lines(path, Charset.forName("utf-8")).forEach(line -> System.out.println(line));

 

            FileStore fileStore = Files.getFileStore(path);

            System.out.println("共用空间:" + fileStore.getTotalSpace());

            System.out.println("可用空间:" + fileStore.getUsableSpace());

        catch (IOException e) {

            e.printStackTrace();

        }

 

    }

}

3、FileVisitor遍历目录和文件

FileVisitor是一个接口,默认实现类SimpleFileVisitor。FileVisitor中的方法:

1、访问目录前触发的方法

FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)

2、访问文件时触发的方法

FileVisitResult visitFile(T file, BasicFileAttributes attrs)

3、访问文件失败时触发的方法

FileVisitResult visitFileFailed(T file, IOException exc)

4、访问目录后触发的方法

FileVisitResult postVisitDirectory(T dir, IOException exc)
 

其返回值是 FileVisitResult, FileVisitResult是个枚举:

public enum FileVisitResult {

    /**

 * Continue. When returned from a {@link FileVisitor#preVisitDirectory

 * preVisitDirectory} method then the entries in the directory should also

 * be visited.

 */

 CONTINUE,

    /**

 * Terminate.

 */

 TERMINATE,

    /**

 * Continue without visiting the entries in this directory. This result

 * is only meaningful when returned from the {@link

 * FileVisitor#preVisitDirectory preVisitDirectory} method; otherwise

 * this result type is the same as returning {@link #CONTINUE}.

 */

 SKIP_SUBTREE,

    /**

 * Continue without visiting the <em>siblings</em> of this file or directory.

 * If returned from the {@link FileVisitor#preVisitDirectory

 * preVisitDirectory} method then the entries in the directory are also

 * skipped and the {@link FileVisitor#postVisitDirectory postVisitDirectory}

 * method is not invoked.

 */

 SKIP_SIBLINGS;

}


4、使用WatchService监控文件变化

考虑这样一个需求:需要监控文件的变化,那该怎样做呢?常规的做法就是启动一个线程(定时任务),每隔一段时间去『遍历』一次指定的目录的文件,如果遍历的结果和上次不同,则认为文件发生了变化。但是有没有更好的方式来实现呢?

NIO2利用WatchService类提供了如下一个监听文件系统的变化的方法:

示例:

import java.io.IOException;

import java.nio.file.FileSystems;

import java.nio.file.Paths;

import java.nio.file.StandardWatchEventKinds;

import java.nio.file.WatchEvent;

import java.nio.file.WatchKey;

import java.nio.file.WatchService;

 

/**

 * WatchService代表一个文件系统监听服务,负责监听path表示的路径下文件变化,

 * 一旦使用了register()方法注册之后,接下里就可以调用watchService的方法来获取被监听的文件变化事件

 */

public class WatchServiceTest {

    public static void main(String[] args) {

        try {

            WatchService watchService = FileSystems.getDefault().newWatchService();

            Paths.get("/Users/qinwenjing/Documents/Projects/Test/src/main/java")

                    .register(watchService,

                            StandardWatchEventKinds.ENTRY_CREATE,

                            StandardWatchEventKinds.ENTRY_DELETE,

                            StandardWatchEventKinds.ENTRY_MODIFY);

 

            while (true) {

                // 获取下一个文件变化事件 (获取下一个WatchKey),如果没有发生就等待

                WatchKey key = watchService.take();

                // 获取下一个文件变化事件(获取下一个WatchKey),没有的话就返回null

                // WatchKey key = watchService.poll();

                System.out.println("拿到watchKey....");

                for (WatchEvent event : key.pollEvents()) {

                    System.out.println(event.context() + " 文件发生了" + event.kind() + "事件!");

                }

                // 重设WatchKey

                boolean valid = key.reset();

                if (!valid) {

                    break;

                }

            }

        catch (IOException e) {

            e.printStackTrace();

        catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}

 

5、访问文件属性的一些高级方式

早期的File类可以访问一些简单的文件属性,比如文件大小、修改时间、文件是否隐藏、是文件还是目录等。如果需要获取或修改更多的文件属性,这是可以利用Java 7的NIO2在java.nio.file.attribute包下提供的大量工具类。

示例:

import java.io.File;

import java.io.IOException;

import java.nio.ByteBuffer;

import java.nio.file.FileSystems;

import java.nio.file.Files;

import java.nio.file.Path;

import java.nio.file.Paths;

import java.nio.file.attribute.BasicFileAttributeView;

import java.nio.file.attribute.BasicFileAttributes;

import java.nio.file.attribute.DosFileAttributeView;

import java.nio.file.attribute.FileOwnerAttributeView;

import java.nio.file.attribute.UserDefinedFileAttributeView;

import java.nio.file.attribute.UserPrincipal;

import java.util.Date;

import java.util.List;

 

public class AttributeViewTest {

    public static void main(String[] args) {

        Path testPath = Paths.get("/Users/qinwenjing/Documents/Projects/Test/src/main/java/IO/NIO2/AttributeViewTest"

                ".java");

        File file = new File("/Users/qinwenjing/Documents/Projects/Test/src/main/java/IO/NIO2/AttributeViewTest"

                ".java");

 

        try {

            // 1、获取访问 基本属性 的BasicFileAttributeView

            BasicFileAttributeView baseView = Files.getFileAttributeView(testPath, BasicFileAttributeView.class);

            // 获取访问基本属性的BasicFileAttributes

            BasicFileAttributes basicFileAttributes = baseView.readAttributes();

            System.out.println("创建时间   :" + basicFileAttributes.creationTime().toMillis());

            System.out.println("最后访问时间:" + basicFileAttributes.lastAccessTime().toMillis());

            System.out.println("最后修改时间:" new Date(basicFileAttributes.lastModifiedTime().toMillis()));

 

            // 2、获取访问 文件属主信息 的FileOwnerAttributeView

            FileOwnerAttributeView ownerView = Files.getFileAttributeView(testPath,

                    FileOwnerAttributeView.class);

            // 获取文件所属的用户

            System.out.println(ownerView.getOwner());

 

            // 获取系统中qinwenjing对应的用户

            UserPrincipal user = FileSystems.getDefault().getUserPrincipalLookupService().lookupPrincipalByName

                    ("qinwenjing");

            // 修改用户

            ownerView.setOwner(user);

 

            // 3、获取访问Dos属性的DosFileAttributeView

            DosFileAttributeView dosView = Files.getFileAttributeView(testPath, DosFileAttributeView.class);

            // 将文件设置为隐藏、只读

            dosView.setHidden(true);

            dosView.setReadOnly(true);

 

 

            // 4、获取访问自定义属性的UserDefinedFileAttributeView(可能为null)

            UserDefinedFileAttributeView userView = Files.getFileAttributeView(testPath, UserDefinedFileAttributeView

                    .class);

            List<String> attributes = userView.list();

            for (String name : attributes) {

                ByteBuffer buf = ByteBuffer.allocate(userView.size(name));

            }

 

        catch (IOException e) {

            e.printStackTrace();

        }

    }

}

 

 

<think>好的,我现在需要帮助用户了解TomcatNIONIO2的区别及用法。首先,我应该回顾一下自己关于Tomcat连接器的知识。Tomcat支持多种I/O模型,包括BIO、NIONIO2和APR。不过,BIO在较新版本中已经被移除了,所以用户可能主要关心NIONIO2。 接下来,我需要确认NIONIO2的基本概念。Java的NIO(New I/O)基于通道(Channel)和选择器(Selector),使用非阻塞I/O,适用于高并发场景。而NIO2(也称为AIO)引入了异步I/O,使用CompletionHandler回调机制,理论上在处理大量连接时更高效,尤其是在I/O操作完成时通知,减少线程占用。 然后,用户的问题是关于配置和使用上的区别。在Tomcat中,配置连接器是通过server.xml文件中的Connector元素。对于NIO,协议是org.apache.coyote.http11.Http11NioProtocol,而NIO2则是org.apache.coyote.http11.Http11Nio2Protocol。需要指出具体的配置示例,这样用户能清楚如何切换。 另外,性能方面的差异也很重要。NIO使用非阻塞I/O,基于事件驱动,适合连接数多但请求处理不耗时的场景。而NIO2的异步I/O可能在处理大量长连接或高延迟I/O操作时表现更好,因为它减少了线程等待时间。不过实际效果可能因操作系统和Java版本而异,尤其是NIO2需要底层操作系统支持异步I/O,比如Linux的epoll可能不支持某些特性,这可能影响实际使用效果。 还要提到版本兼容性和依赖。例如,Tomcat 8及以后版本对NIO2的支持更好,而Java 7+是必须的。同时,用户需要注意Tomcat Native Library的版本,比如引用中提到的1.2.21修复了内存泄漏问题,这可能影响NIO/NIO2与OpenSSL配合使用时的稳定性。 在回答时,需要结构清晰,分点说明区别、配置方法、性能对比和使用场景。同时,根据用户提供的引用,提到更新Native库的重要性,以及可能涉及的加密拦截器,但主要聚焦在NIONIO2的配置和使用上。 最后,生成的相关问题要围绕Tomcat的其他I/O模型、性能调优、连接数优化和SSL集成等方面,帮助用户扩展了解相关内容。确保回答准确,并正确引用提供的参考资料,特别是关于内存泄漏修复的部分。</think>### TomcatNIONIO2的区别及用法 #### 1. **核心区别** - **NIO (Non-blocking I/O)** 基于Java的`java.nio`包实现,采用多路复用技术(如Selector)处理并发连接,线程池管理请求,适用于高并发短连接场景。其协议配置为`org.apache.coyote.http11.Http11NioProtocol`[^1]。 - **NIO2 (Asynchronous I/O)** 基于Java 7+的`java.nio.channels.AsynchronousChannel`,通过回调机制实现异步I/O操作,减少线程阻塞。适用于长连接或I/O密集型操作,协议配置为`org.apache.coyote.http11.Http11Nio2Protocol`[^1]。 #### 2. **配置方法** 在`server.xml`中修改Connector配置: ```xml <!-- NIO配置 --> <Connector protocol="org.apache.coyote.http11.Http11NioProtocol" port="8080" maxThreads="200" .../> <!-- NIO2配置 --> <Connector protocol="org.apache.coyote.http11.Http11Nio2Protocol" port="8080" maxThreads="200" .../> ``` #### 3. **性能对比** - **NIO**:通过事件驱动减少线程开销,但在I/O等待时仍需占用线程资源。 - **NIO2**:异步回调机制释放线程资源,理论上在高延迟操作中更高效,但依赖操作系统支持(如Linux需内核2.6+)[^1]。 #### 4. **使用场景** - **NIO**:Web应用请求响应快、连接数多(如API服务)。 - **NIO2**:文件上传、流媒体传输等长耗时I/O操作。 #### 5. **注意事项** - NIO2需Java 7+及兼容的Tomcat版本(推荐Tomcat 8.5+)。 - 使用`Tomcat Native Library`(如1.2.21版本)可优化NIO/NIO2与SSL的性能,并修复内存泄漏问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值