首页 > 代码库 > java nio

java nio

NIO 是java nonblocking(非阻塞) IO 的简称,在jdk1.4 里提供的新api 。Sun 官方标榜的特性如下: 为所有的原始类型提供(Buffer)缓存支持。字符集编码解码解决方案。 Channel :一个新的原始I/O 抽象。 支持锁和内存映射文件的文件访问接口。 提供多路(non-bloking) 非阻塞式的高伸缩性网络I/O 。

Java NIO非堵塞应用通常适用用在I/O读写等方面,我们知道,系统运行的性能瓶颈通常在I/O读写,包括对端口和文件的操作上,之前,在打开一个I/O通道后,read()将一直等待在端口一边读取字节内容,如果没有内容进来,read()也是傻傻的等,这会影响我们程序继续做其他事情,那么改进做法就是开设线程,让线程去等待,但是这样做也是相当耗费资源的。
Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。

NIO包(java.nio.*)引入了四个关键的抽象数据类型,它们共同解决传统的I/O类中的一些问题。
1. Buffer:它是包含数据且用于读写的线形表结构。其中还提供了一个特殊类用于内存映射文件的I/O操作。
2. Charset:它提供Unicode字符串影射到字节序列以及逆影射的操作。
3. Channels:包含socket,file和pipe三种管道,它实际上是双向交流的通道。
4. Selector:它将多元异步I/O操作集中到一个或多个线程中(它可以被看成是Unix中select()函数或Win32中WaitForSingleEvent()函数的面向对象版本)。


Package java.nio

这个包主要定义了 Buffer 及其子类。 Buffer 定义了一个线性存放 primitive type 数据的容器接口。对于除 boolean 以外的其他 primitive type ,都有一个相应的 Buffer 子类, ByteBuffer 是其中最重要的一个子类。值得注意的是 Buffer 及其子类都不是线程安全的。


Package java.nio.channels

这个包定义了 Channel 的概念, Channel 表现了一个可以进行 IO 操作的通道(比如,通过 FileChannel ,我们可以对文件进行读写操作)。 java.nio.channels 包含了文件系统和网络通讯相关的 channel 类。这个包通过 Selector 和 SelectableChannel 这两个类,还定义了一个进行非阻塞( non-blocking ) IO 操作的 API ,这对需要高性能 IO 的应用非常重要。非阻塞 IO 允许应用程序同时监控多个 channel 以提高性能,这一功能是通过 Selector , SelectableChannel 和 SelectionKey 这 3 个类来实现的。


Package java.nio.charset

这个包定义了 字符编码解码 : 字节码本身只是一些数字,放到正确的上下文中被正确被解析。向 ByteBuffer 中存放数据时需要考虑字符集的编码方式,读取展示 ByteBuffer 数据时涉及对字符集解码。以我们最常见的 http 请求为例,在请求的时候必须对请求进行正确的编码。在得到响应时必须对响应进行正确的解码。


Selector

NIO 有一个主要的类Selector,这个类似一个观察者,只要我们把需要探知的socketchannel告诉Selector,我们接着做别的事情,当有事件发生时,他会通知我们,传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的socketchannel,然后,我们从这个Channel中读取数据,放心,包准能够读到,接着我们可以处理这些数据。

 

//文件上传
package tesrt;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class Nio {

    /**
     *
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        String infile = "D:\\copy.sql";
        String outfile = "D:\\copy.txt";
        try {
            // 获取源文件和目标文件的输入输出流
            FileInputStream fin = new FileInputStream(infile);
            FileOutputStream fout = new FileOutputStream(outfile);
            // 获取输入输出通道
            FileChannel fcin = fin.getChannel();
            FileChannel fcout = fout.getChannel();

            // 创建缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while (true) {
                // clear方法重设缓冲区,使它可以接受读入的数据
                buffer.clear();
                // 从输入通道中将数据读到缓冲区
                int r = fcin.read(buffer);
                // read方法返回读取的字节数,可能为零,如果该通道已到达流的末尾,则返回-1
                if (r == -1) {
                    break;
                }
                // flip方法让缓冲区可以将新读入的数据写入另一个通道
                buffer.flip();
                // 从输出通道中将数据写入缓冲区
                fcout.write(buffer);
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}



//NIO中读取数据的步骤:1)从FileInputStream中得到Channel对象;2)创建一个buffer对象;3)从Channel中读数据到Buffer中;

200M文件读取 大约2,3分钟

package tesrt;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class Nio {

    public static void main(String args[]) throws Exception {
        int bufSize = 100;
        File fin = new File("D:\\数据库\\mysql工具\\mysql-5.5.8-win32.msi");
        File fout = new File("D:\\ysql-5.5.8-win32.msi");

        FileChannel fcin = new RandomAccessFile(fin, "r").getChannel();
        ByteBuffer rBuffer = ByteBuffer.allocate(bufSize);

        FileChannel fcout = new RandomAccessFile(fout, "rws").getChannel();
        ByteBuffer wBuffer = ByteBuffer.allocateDirect(bufSize);

        readFileByLine(bufSize, fcin, rBuffer, fcout, wBuffer);

        System.out.print("OK!!!");
    }

    public static void readFileByLine(int bufSize, FileChannel fcin,
            ByteBuffer rBuffer, FileChannel fcout, ByteBuffer wBuffer) {
        String enterStr = "\n";
        try {
            byte[] bs = new byte[bufSize];

            int size = 0;
            StringBuffer strBuf = new StringBuffer("");
            // while((size = fcin.read(buffer)) != -1){
            while (fcin.read(rBuffer) != -1) {
                int rSize = rBuffer.position();
                rBuffer.rewind();
                rBuffer.get(bs);
                rBuffer.clear();
                String tempString = new String(bs, 0, rSize);
                // System.out.print(tempString);
                // System.out.print("<200>");

                int fromIndex = 0;
                int endIndex = 0;
                while ((endIndex = tempString.indexOf(enterStr, fromIndex)) != -1) {
                    String line = tempString.substring(fromIndex, endIndex);
                    line = new String(strBuf.toString() + line);
                    // System.out.print(line);
                    // System.out.print("</over/>");
                    // write to anthone file
                    writeFileByLine(fcout, wBuffer, line);
                    strBuf.delete(0, strBuf.length());
                    fromIndex = endIndex + 1;
                }
                if (rSize > tempString.length()) {
                    strBuf.append(tempString.substring(fromIndex,
                            tempString.length()));
                } else {
                    strBuf.append(tempString.substring(fromIndex, rSize));
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static void writeFileByLine(FileChannel fcout, ByteBuffer wBuffer,
            String line) {
        try {
            fcout.write(wBuffer.wrap(line.getBytes()), fcout.size());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}



NIO中还有一个知识点就是无阻塞的Socket编程,这里就不说了,因为比较复杂,但是如果我们真正理解了Selector这个调度者的工作,那么无阻塞的实现机制我们差不多就掌握了。