JAVA NIO入门讲解1

本文介绍了JAVA NIO的核心组件:通道(Channel)、缓冲区(Buffer)和选择器(Selector)。重点讲解了FileChannel、MappedByteBuffer、ServerSocketChannel及其在数据交换、线程间通信中的应用。同时,探讨了缓冲区的分类、原理及使用,以及Selector在多路复用中的作用。此外,还讨论了NIO中的中文乱码问题以及内存映射文件的性能优势与限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NIO基本概述[^3]

NIO主要围绕三个核心组件:Channel、Buffer、Selector;以及若干核心工具类:Pipe、FileLock、Charset、Files、Channels(用于IO和NIO转换)

  • NIO有两个核心概念,即==通道(channel)和缓冲区(buffer)==,其核心就是从通道中读取数据到缓冲区,或者从缓冲区中读取数据写入通道。
  • IO的核心概念主要是字符流和字节流,因此NIO面向的是缓冲区,而IO直接就是面对流。
  • NIO是非阻塞的(Non-Blocking IO),==只要把数据交给缓冲区,剩下的就可以不用管了==,直接用线程去处理其他事情就可以。而阻塞模型的话,线程必须阻塞等待流的IO完成。
  • NIO可以使用Selector实现多路复用功能。
  • 通道是全双工的,可以实现异步读写,而流是单向的,只能单向读或单向写,也就是单工的。
  • JDK1.4提供了对非阻塞IO(NIO)的支持,JDK1.5_update10版本使用epoll替代了传统的select/poll,极大的提升了NIO通信的性能。Java 1.7 增加了异步IO

Channel通道

主要分以下几个具体实现类,分别针对不同的实现,比如文件、UDP、TCP。

  • FileChannel - 文件,路径参看[^6]
  • DatagramChannel – UDP
  • SocketChannel – TCP
  • ServerSocketChannel – TCP,监听网络请求,为每个请求建立SocketChannel

这里写图片描述

FileChannel

该类是一个抽象类,但其对象是线程安全的,多个线程运行并发调用该对象。

影响通道位置或者影响文件大小的操作都是单线程的(single-threaded)。如果有一个线程已经在执行会影响通道位置或文件大小的操作,那么其他尝试进行此类操作之一的线程必须等待。并发行为也会受到底层的操作系统或文件系统影响。[^7]

因为当前操作系统大部分的文件I/O还不支持异步请求,因而大部分情况下的FileChannel总是阻塞式的。该通道不能直接创建,一个FileChannel只能读写一个File对象,具有和File对象相同的文件访问权限,且只能通过调用File对象(RandomAccessChannel、FileInputStream、FileOutputStream)的getChannel方法获取[^7]。

简单示例

读文件的具体案例可以参看“Buffer缓存区的简单示例”。写文件的案例如下[^9][^10]:

/**
 * 将内容写入文件中,如果文件已经存在,则附加在后面,否则新建文件
 *
 * @param stringContent
 * @param filePath
 * @param isAppend      判断是否追加还是覆盖
 */
public static boolean writeStringToFile(final String stringContent, final String filePath, final boolean isAppend) {
    return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
        @Override
        public Boolean run() {
            if (StringUtils.isBlank(stringContent)) {
                return false;
            }
            File file = checkFile(filePath);
            if (file == null) {
                //文件读取失败
                return false;
            }
            RandomAccessFile randomAccessFile = null;
            FileChannel fileChannel = null;
            ByteBuffer byteBuffer = null;
            try {
                randomAccessFile = new RandomAccessFile(file, "rw");
                fileChannel = randomAccessFile.getChannel();
                byte[] bytes = stringContent.getBytes(Charset.forName("utf-8"));
                byteBuffer = ByteBuffer.wrap(bytes);
                int byteLength = bytes.length, length = 0;
                if (isAppend) {
                    //判断是追加
                    fileChannel.position(file.length());
                } else {
                    fileChannel.position(0);
                    //强制指定长度
                    fileChannel.truncate(byteLength);
                }
                while (byteBuffer.hasRemaining()) {
                    fileChannel.write(byteBuffer);
                }
                //强制刷新到文件系统
                fileChannel.force(true);
                return true;
            } catch (FileNotFoundException e) {
                //文件读取失败
                return false;
            } catch (IOException e) {
                //写文件失败
                return false;
            } finally {
                if (randomAccessFile != null) {
                    try {
                        randomAccessFile.close();
                    } catch (IOException e) {
                        ;
                    }
                }
                if (fileChannel != null) {
                    try {
                        fileChannel.close();
                    } catch (IOException e) {
                        ;
                    }
                }
                if (byteBuffer != null) {
                    byteBuffer.clear();
                }
            }
        }
    });
}

    /**
     * 判断当前文件是否存在,如果不存在则创建,否则直接返回File描述文件对象
     * <pre>
     *     注意:如果该文件是一个目录,则直接返回null
     * </pre>
     *
     * @param filePath
     * @return
     */
    public static File checkFile(final String filePath) {

        if (StringUtils.isNotBlank(filePath)) {
            final File file = new File(filePath);
            if (file.isDirectory()) {
                //如果是目录,则直接返回null
                return null;
            }
            if (file.exists()) {
                //如果文件已经存在,则直接返回文件描述对象
                return file;
            }else{
                //如果文件不存在,则直接创建文件
                return AccessController.doPrivileged(new PrivilegedAction<File>() {
                    @Override
                    public File run() {
                        try {
                            boolean success = file.createNewFile();
                            if(success){
                                return file;
                            }
                            return null;
                        } catch (IOException e) {

                        }
                        return null;
                    }
                });
            }
        }

        return null;
    }

注意[^8][^10]

  • FileDescriptor类是读写文件的核心,代表一个独立于系统的隐式句柄流。看下面的JDK说明:

Instances of the file descriptor class serve as an opaque handle to the underlying machine-specific structure representing an open file, an open socket, or another source or sink of bytes. The main practical use for a file descriptor is to create a FileInputStream or FileOutputStream to contain it.
FileDescriptor类作为一个隐式句柄,可用于代表与底层机器适配的一个打开文件、一个打开的socket,另一个数据源或字节槽。最主要的用法是用于创建FileInputStream或FileOutputStream以包含它。(注:我们常用的System.in/System.out/System.err就是包含了这个)

  • File类是一个独立于系统的、不可变的(immutable)目录或文件分层路径名抽象(An abstract representation of file and directory pathnames),可用于“抽象路径名(an abstract pathname)与路径字符串(a pathname string)的转换”(路径包含绝对路径和相对路径,前者说明定位文件或目录不需要额外信息,而后者还需要系统属性user.dir配合,user.dir属性是指当前应用JVM启动的地址),也可以描述文件和目录信息,比如文件大小、名称、是否存在、获取父亲目录地址(getParent)等信息,不能用于改变或读写文件内容,读写内容是通过FileDescriptor句柄流实现的。java.nio.file包提供了更加丰富的抽象及工具类。
  • Ra
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值