深入理解JavaI/O流

其实我个人觉得,javaI/O流是一种比较乏味的事情。因为看不到明显的运行效果,但是输入输出程序都需要用到I/O操作即:允许程序读取外部数据、用户输入数据,使用输出机制允许程序记录运行状态,将数据仅处理。Java中的I/O通过java.io中的类和接口支持,java中的IO流包括字符流和字节流,两大类除此之外java的IO中还是用了一种模式:装饰模式

将IO流分成底层的节点流和上层的处理流,其中节点流用于和底层的物理存储节点直接关联,不同的物理节点获取节点流的方式可能存在一定的差异,但程序可以把不同的物物理节点流包装成统一的处理流从,从而允许程序使用统一的输入、输出代码都区不同物理存储节点资源。

Java7在java.nio及其子包下提供了一系列的全新的API这些API对原有新IO进行了升级,因此被称为NIO2,通过这些NIO2程序可以更高效的进行输入、输出操作接下来我也会对这些新的特性进行介绍。

File类:

下面介绍一些访问文件名相关的方法:

boolean canExecute()

Tests whether the application can execute the file denoted by this abstract pathname.//测试应用程序是否可以执行用此抽象路径名表示的文件。

boolean canRead()

Tests whether the application can read the file denoted by this abstract pathname.//测试应用程序是否可以读取文件用这种抽象的路径名。

boolean canWrite()

Tests whether the application can modify the file denoted by this abstract pathname.Tests whether the application can modify the file denoted by this abstract pathname.//测试应用程序是否可以修改用此抽象路径名表示的文件。测试应用程序是否可以修改用此抽象路径名表示的文件

int compareTo(File pathname)

Compares two abstract pathnames lexicographically.//比较两个抽象的路径名字母顺序进行。

boolean createNewFile()

Atomically creates a new, empty file named by this abstract pathname if and only if a file with this name does not yet exist.//自动创建一个新的空文件被这个抽象路径名当且仅当一个文件,这个名字不存在。

static File createTempFile(String prefix, String suffix)

Creates an empty file in the default temporary-file directory, using the given prefix and suffix to generate its name.//在默认临时文件目录中创建一个空文件,使用给定的前缀和后缀来生成它的名字。

static File createTempFile(String prefix, String suffix, File directory)

Creates a new empty file in the specified directory, using the given prefix and suffix strings to generate its name.//创建一个新的空文件在指定的目录中,使用给定的前缀和后缀字符串生成它的名字。

boolean delete()

Deletes the file or directory denoted by this abstract pathname.//删除文件或目录用这种抽象的路径名。

void deleteOnExit()

Requests that the file or directory denoted by this abstract pathname be deleted when the virtual machine terminates.Requests that the file or directory denoted by this abstract pathname be deleted when the virtual machine terminates.//请求的文件或目录用这个抽象路径名虚拟机终止时被删除。请求的文件或目录用这个抽象路径名虚拟机终止时被删除。

boolean equals(Object obj)

Tests this abstract pathname for equality with the given object.//测试此抽象路径名的平等与给定的对象。

boolean exists()

Tests whether the file or directory denoted by this abstract pathname exists.//检查文件或目录是否用这个抽象路径名的存在。

File getAbsoluteFile()

Returns the absolute form of this abstract pathname.//返回此抽象路径名的绝对形式。

String getAbsolutePath()

Returns the absolute pathname string of this abstract pathname.//返回此抽象路径名的绝对路径名字符串。

File getCanonicalFile()

Returns the canonical form of this abstract pathname.//返回此抽象路径名的规范形式。

String getCanonicalPath()

Returns the canonical pathname string of this abstract pathname.//返回此抽象路径名的规范路径名字符串。

long getFreeSpace()

Returns the number of unallocated bytes in the partition named by this abstract path name.//返回分区命名中未分配的字节数的抽象路径名。

String getName()

Returns the name of the file or directory denoted by this abstract pathname.//返回的文件或目录的名称用这种抽象的路径名

String getParent()

Returns the pathname string of this abstract pathname‘s parent, or null if this pathname does not name a parent directory.//返回此抽象路径名的路径名字符串的母公司或null如果这个路径名不名一个父目录。

File getParentFile()

Returns the abstract pathname of this abstract pathname‘s parent, or null if this pathname does not name a parent directory.//返回此抽象路径名的抽象路径名的母公司或null如果这个路径名不名一个父目录

String getPath()

Converts this abstract pathname into a pathname string.//将此抽象路径名转换为一个路径名字符串

long getTotalSpace()

Returns the size of the partition named by this abstract pathname.//返回分区的大小被这抽象的路径名。

long getUsableSpace()

Returns the number of bytes available to this virtual machine on the partition named by this abstract pathname.//返回此虚拟机可用的字节数的分区命名这个抽象的路径名。

int hashCode()

Computes a hash code for this abstract pathname.//计算哈希代码抽象路径名。

boolean isAbsolute()

Tests whether this abstract pathname is absolute.//测试此抽象路径名是否绝对的。

boolean isDirectory()

Tests whether the file denoted by this abstract pathname is a directory.//测试文件用此抽象路径名是否一个目录

boolean isFile()

Tests whether the file denoted by this abstract pathname is a normal file.//测试文件用此抽象路径名是否正常的文件。

boolean isHidden()

Tests whether the file named by this abstract pathname is a hidden file.//测试文件是否被这个抽象路径名是一个隐藏文件。

long lastModified()

Returns the time that the file denoted by this abstract pathname was last modified.//返回文件的时候用这个抽象路径名是最后修改。

long length()

Returns the length of the file denoted by this abstract pathname.//返回文件的长度用这种抽象的路径名。

String[] list()

Returns an array of strings naming the files and directories in the directory denoted by this abstract pathname.//返回一个字符串数组命名目录中的文件和目录用这种抽象的路径名。

String[] list(FilenameFilter filter)

Returns an array of strings naming the files and directories in the directory denoted by this abstract pathname that satisfy the specified filter.//返回一个字符串数组命名目录中的文件和目录用这个抽象路径名满足指定过滤器

File[] listFiles()

Returns an array of abstract pathnames denoting the files in the directory denoted by this abstract pathname.//返回一个抽象路径名数组表示目录中的文件用这种抽象的路径名。

File[] listFiles(FileFilter filter)

Returns an array of abstract pathnames denoting the files and directories in the directory denoted by this abstract pathname that satisfy the specified filter.//返回一个数组抽象路径名表示的目录中的文件和目录用这个抽象路径名满足指定过滤器。

File[] listFiles(FilenameFilter filter)

Returns an array of abstract pathnames denoting the files and directories in the directory denoted by this abstract pathname that satisfy the specified filter.//返回一个数组抽象路径名表示的目录中的文件和目录用这个抽象路径名满足指定过滤器。

static File[] listRoots()

List the available filesystem roots.//列出可用的文件系统根。

boolean mkdir()

Creates the directory named by this abstract pathname.//创建目录命名这个抽象的路径名。

boolean mkdirs()

Creates the directory named by this abstract pathname, including any necessary but nonexistent parent directories.//创建此抽象路径名所命名的目录,包括任何必要的但不存在的父目录。

boolean renameTo(File dest)

Renames the file denoted by this abstract pathname.//重命名文件用这种抽象的路径名。

boolean setExecutable(boolean executable)

A convenience method to set the owner‘s execute permission for this abstract pathname.//一个方便的方法来设置这个抽象路径名的所有者的执行权限。

boolean setExecutable(boolean executable, boolean ownerOnly)

Sets the owner‘s or everybody‘s execute permission for this abstract pathname.//设置所有者或每个人都对这个抽象路径名的执行权限

boolean setLastModified(long time)

Sets the last-modified time of the file or directory named by this abstract pathname.//设置文件或目录的最后修改时间被这抽象的路径名

boolean setReadable(boolean readable)

A convenience method to set the owner‘s read permission for this abstract pathname.//一个方便的方法来设置这个抽象路径名的所有者的读权限。

boolean setReadable(boolean readable, boolean ownerOnly)

Sets the owner‘s or everybody‘s read permission for this abstract pathname.//设置所有者或每个人都对这个抽象路径名的读权限。

boolean setReadOnly()

Marks the file or directory named by this abstract pathname so that only read operations are allowed.//标志着文件或目录被这个抽象路径名,只允许读取操作。

boolean setWritable(boolean writable)

A convenience method to set the owner‘s write permission for this abstract pathname.//一个方便的方法来设置这个抽象路径名的所有者的写权

boolean setWritable(boolean writable, boolean ownerOnly)

Sets the owner‘s or everybody‘s write permission for this abstract pathname.//设置所有者或每个人都对这个抽象路径名的写权限。

Path toPath()

Returns a java.nio.file.Path object constructed from the this abstract path.//返回一个java.nio.file。路径对象由这个抽象路径。

String toString()

Returns the pathname string of this abstract pathname.//返回此抽象路径名的路径名字符串。

URI toURI()

Constructs a file: URI that represents this abstract pathname.//构造一个文件:URI表示此抽象路径名

URL toURL()

Deprecated.

This method does not automatically escape characters that are illegal in URLs. It is recommended that new code convert an abstract pathname into a URL by first converting it into a URI, via the toURI method, and then converting the URI into a URL via the
URI.toURL method.//弃用。  这个方法不会自动转义字符在url是非法的。建议新代码抽象路径名转换为一个URL的方法是,首先将它转换为一个URI,通过toURI方法,然后通过URI URI转换成一个URL。toURL方法。

理解java的IO流:

java的IO流试试先输入输出的基础在java中把不同的输入/输出源抽象的表示为流。

按照输入和输出分:

输入流:只能从中读取数据而不能向其中写入数据。

输出流:只能向其中写入数据不能从中读取数据。

输入流主要由InputStream和Reader作为基类,而输出流主要由OutputStream和Writer作为基类。他们都是一些抽象基类不能直接实例化。

按照流的角色进行分类:

节点流:也被称作低级流因为只能从特殊的IO设备读写数据。

处理流:经过包装的节点流通过封装后实现数据的读取功能。

使用处理流的好处就是:只要使用相同的处理流,程序就可以使用完全相同的代码访问不同的数据源,随着处理流所包装节点流的变化,程序所访问的数据源也发生变化。

InputStream:得到的是字节输入流,InputStream.read("filename")之后,得到字节流

Reader:读取的是字符流

InputStreamReader:从字节到字符的桥梁。InputStreamReader(InputStream.read("filename"));

reader.read(InputStreamReader(InputStream in));便可从字节变为字符,打印显示了。

java.io.Reader 和 java.io.InputStream 组成了Java 输入类。

Reader 用于读入16位字符,也就是Unicode 编码的字符;而 InputStream 用于读入 ASCII 字符和二进制数据。

Reader支持16位的Unicode字符输出,

InputStream支持8位的字符输出。

Reader和InputStream分别是I/O库提供的两套平行独立的等级机构,

1byte = 8bits

InputStream、OutputStream是用来处理8位元的流,

Reader、Writer是用来处理16位元的流。

而在JAVA语言中,byte类型是8位的,char类型是16位的,所以在处理中文的时候需要用Reader和Writer。

值得说明的是,在这两种等级机构下,还有一道桥梁InputStreamReader、OutputStreamWriter负责进行InputStream到Reader的适配和由OutputStream到Writer的适配。

在 Java中,有不同类型的 Reader 输入流对应于不同的数据源:

FileReader 用于从文件输入; CharArrayReader 用于从程序中的字符数组输入; StringReader 用于从程序中的字符串输入; PipedReader 用于读取从另一个线程中的 PipedWriter 写入管道的数据。

相应的也有不同类型的 InputStream 输入流对应于不同的数据源:FileInputStream,ByteArrayInputStream,StringBufferInputStream,PipedInputStream。

另外,还有两种没有对应 Reader 类型的 InputStream 输入流: Socket 用于套接字; URLConnection 用于 URL 连接。 这两个类使用 getInputStream() 来读取数据。

相应的,java.io.Writer 和 java.io.OutputStream 也有类似的区别。

关于InputStream.read(byte[] b)和InputStream.read(byte[] b,int off,int len)这两个方法都是用来从流里读取多个字节的,有经验的程序员就会发现,这两个方法经常 读取不到自己想要读取的个数的字节。比如第一个方法,程序员往往希望程序能读取到b.length个字节,而实际情况是,系统往往读取不了这么多。仔细阅读Java的API说明就发现了,这个方法 并不保证能读取这么多个字节,它只能保证最多读取这么多个字节(最少1个)。因此,如果要让程序读取count个字节,最好用以下代码:

byte[] b = new byte[count];

int readCount = 0; // 已经成功读取的字节的个数

while (readCount < count) {

readCount += in.read(bytes, readCount, count - readCount);

}

用这段代码可以保证读取count个字节,除非中途遇到IO异常或者到了数据流的结尾(EOFException)

1.InputStream

◇ 从流中读取数据:

int read( ); //读取一个字节,返回值为所读的字节

int read( byte b[ ] ); //读取多个字节,放置到字节数组b中,通常读取的字节数量为b的长度,返回值为实际读取的字节的数量

int read( byte b[ ], int off, int len ); //读取len个字节,放置到以下标off开始字节数组b中,返回值为实际读取的字节的数量

int available( );   //返回值为流中尚未读取的字节的数量

long skip( long n ); //读指针跳过n个字节不读,返回值为实际跳过的字节数量

◇ 关闭流:

close( ); //流操作完毕后必须关闭

◇ 使用输入流中的标记:

void mark( int readlimit ); //记录当前读指针所在位置,readlimit 表示读指针读出readlimit个字节后所标记的指针位置才失效

void reset( );     //把读指针重新指向用mark方法所记录的位置

boolean markSupported( ); //当前的流是否支持读指针的记录功能

有关每个方法的使用,详见java API。

2.OutputStream

◇ 输出数据:

void write( int b );   //往流中写一个字节b

void write( byte b[ ] ); //往流中写一个字节数组b

void write( byte b[ ], int off, int len ); //把字节数组b中从下标off开始,长度为len的字节写入流中

◇ flush( )  //刷空输出流,并输出所有被缓存的字节,由于某些流支持缓存功能,该方法将把缓存中所有内容强制输出到流中。

◇ 关闭流:

close( );       //流操作完毕后必须关闭

4.4.3 I/O中的例外

  进行I/O操作时可能会产生I/O例外,属于非运行时例外,应该在程序中处理。如:FileNotFoundException, EOFException, IOException

4.5 文件处理

I/O处理中,最常见的是对文件的操作,java.io包中有关文件处理的类有:File、FileInputStream、 FileOutputStream、RamdomAccessFile和FileDescriptor;接口有:FilenameFilter。

4.5.1 文件描述

类File提供了一种与机器无关的方式来描述一个文件对象的属性。下面我们介绍类File中提供的各种方法。

 ◇ 文件或目录的生成

public File(String path);//如果path是实际存在的路径,则该File对象表示的是目录;如果path是文件名,则该File对象表示的是文件。

public File(String path,String name);//path是路径名,name是文件名

public File(File dir,String name);//dir是路径名,name是文件名

◇ 文件名的处理

String getName( ); //得到一个文件的名称(不包括路径)

String getPath( ); //得到一个文件的路径名

String getAbsolutePath( );//得到一个文件的绝对路径名

String getParent( ); //得到一个文件的上一级目录名

String renameTo(File newName); //将当前文件名更名为给定文件的完整路径

◇ 文件属性测试

boolean exists( ); //测试当前File对象所指示的文件是否存在

boolean canWrite( );//测试当前文件是否可写

boolean canRead( );//测试当前文件是否可读

boolean isFile( ); //测试当前文件是否是文件(不是目录)

boolean isDirectory( ); //测试当前文件是否是目录

◇ 普通文件信息和工具

long lastModified( );//得到文件最近一次修改的时间

long length( ); //得到文件的长度,以字节为单位

boolean delete( ); //删除当前文件

◇ 目录操作

boolean mkdir( ); //根据当前对象生成一个由该对象指定的路径

String list( ); //列出当前目录下的文件

代码实例:

常用读文件:

/**

     *以字节为单位读取文件,常用于读二进制文件,如图片、声音、影像等文件。

     *@paramfileName:文件的名

     */

    publicstaticvoid readFileByBytes(String fileName) {

       File file= new File(fileName);

       InputStream in = null;

       try {

           logger.debug("以字节为单位读取文件内容,一次读多个字节:");

            /*一次读多个字节*/

           byte[] tempbytes =newbyte[100];

           int byteread = 0;

           in = new FileInputStream(file);

            /*读入多个字节到字节数组中,byteread为一次读入的字节数*/

           while ((byteread = in.read(tempbytes)) != -1) {

              logger.debug(tempbytes);

              logger.debug(0);

              logger.debug(byteread);

           }

       } catch (Exception e1) {

           logger.error("读取文本文件异常",e1);

       } finally {

           if (in !=null) {

              try {

                  in.close();

              } catch (IOException e1) {

                  logger.error("读取文本文件异常",e1);

              }

           }

       }

    }
/**

     *以字符为单位读取文件,常用于读文本,数字等类型的文件

     *@paramfileName:文件名

     */

    public staticvoid readFileByChars(StringfileName) {

       Reader reader = null;

       try {

           logger.debug("以字符为单位读取文件内容,一次读多个字节:");

            /*一次读多个字符*/

           char[] tempchars =newchar[100];

           int charread = 0;

           if(fileName!=null&&!"".equals(fileName)){

              reader = new InputStreamReader(new FileInputStream(fileName));

               /*读入多个字符到字符数组中,charread为一次读取字符数*/

              while ((charread = reader.read(tempchars)) != -1) {

                   /*对于windows下,rn这两个字符在一起时,表示一个换行。*/

                   /*但如果这两个字符分开显示时,会换两次行。*/

                   /*因此,屏蔽掉r,或者屏蔽n。否则,将会多出很多空行。*/

                  if ((charread == tempchars.length)

                         && (tempchars[tempchars.length - 1] !='r')) {

                     logger.debug(tempchars);

                  } else {

                     for (int i = 0; i < charread; i++) {

                         if (tempchars[i] =='r') {

                            continue;

                         } else {

                            logger.debug(tempchars[i]);

                         }

                     }

                  }

              }

           }

       } catch (Exception e1) {

           logger.error("读取文本文件异常",e1);

       } finally {

           if (reader !=null) {

              try {

                  reader.close();

              } catch (IOException e1) {

                  logger.error("读取文本文件异常",e1);

              }

           }

       }

    }

/**

     *以行为单位读取文件,常用于读面向行的格式化文件

     *@paramfileName:文件名

     */

    publicstatic List<String> readFileByLines(StringfileName) {

       List<String> list = new ArrayList<String>();

       if(fileName!=null&&!"".equals(fileName)){

       File file = new File(fileName);

       BufferedReader reader = null;

       try {

           logger.debug("以行为单位读取文件内容,一次读一整行:");

           reader = new BufferedReader(new FileReader(file));

           String tempString = null;

            /*一次读入一行,直到读入null为文件结束*/

           while ((tempString = reader.readLine()) !=null) {

              logger.debug(tempString);

              list.add(tempString);

           }

       } catch (IOException e) {

           logger.error("读取文本文件异常",e);

       } finally {

           if (reader !=null) {

              try {

                  reader.close();

              } catch (IOException e1) {

                  logger.error("读取文本文件异常",e1);

              }

           }

       }

    }

       return list;

    }

常用写文件:

/**

    *把内容写到文件

    *@paramfilePathName文件名

    *@paramList<String>文件内容

    */

    publicstaticboolean writerFile(String filePathName,String content){

       boolean flag=false;

       OutputStreamWriter osw=null;

       try {

           if(filePathName!=null&&!"".equals(filePathName)){

              osw = new OutputStreamWriter(new FileOutputStream(filePathName));

           }

       } catch (FileNotFoundException e1) {

           flag=false;

           e1.printStackTrace();

       }

       if(osw!=null){

       BufferedWriter bw=new BufferedWriter(osw);

       try {

           if(content!=null&&!"".equals(content)){

              bw.write(content);

              flag= true;

           }

       } catch (IOException e) {

           flag=false;

           e.printStackTrace();

       }finally{

           try {

              bw.close();

              osw.close();

           } catch (IOException e) {

              flag=false;

              e.printStackTrace();

           }         

       }

       }

       return flag;

    }

/**

    *把内容写到文件或追加到文件中

    *@paramfilePathName文件名

    *@paramList<String>文件内容

    */

    publicstaticboolean writerFileIsAppend(String filePathName,String content){

       boolean flag=false;

       OutputStreamWriter osw=null;

       try {

           if (filePathName!=null&&!"".equals(filePathName)) {

              osw = new OutputStreamWriter(new FileOutputStream(filePathName,true));

           }

       } catch (Exception e1) {

           flag=false;

           e1.printStackTrace();

       }

       if(osw!=null){

       BufferedWriter bw=new BufferedWriter(osw);

       try {

           if(content!=null&&!"".equals(content)){

              bw.write(content);

              flag= true;

           }

       } catch (IOException e) {

           flag=false;

           e.printStackTrace();

       }finally{

           try {

              bw.close();

              osw.close();

           } catch (IOException e) {

              flag=false;

              e.printStackTrace();

           }         

       }

       }

       return flag;

    }

七、  注意事项

1、将高级流“套接“在低级流上,这样起到缓冲的作用可以提高效率。

2、将使用完的流关闭,释放资源。

3、读取如图片、声音、影像等文件用字节流。

4、读取如文本等文件用字符流。

5、根据具体的数据格式选择合适的读写方法、如按行读写、按照字节读写等

NOI2介绍:

按照《Unix网络编程》的划分,IO模型可以分为:阻塞IO、非阻塞IO、IO复用、信号驱动IO和异步IO,按照POSIX标准来划分只分为两类:同步IO和异步IO。如何区分呢?首先一个IO操作其实分成了两个步骤:发起IO请求和实际的IO操作,同步IO和异步IO的区别就在于第二个步骤是否阻塞,如果实际的IO读写阻塞请求进程,那么就是同步IO,因此阻塞IO、非阻塞IO、IO服用、信号驱动IO都是同步IO,如果不阻塞,而是操作系统帮你做完IO操作再将结果返回给你,那么就是异步IO。阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞,如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。

Java nio 2.0的主要改进就是引入了异步IO(包括文件和网络),这里主要介绍下异步网络IO API的使用以及框架的设计,以TCP服务端为例。首先看下为了支持AIO引入的新的类和接口:

java.nio.channels.AsynchronousChannel

标记一个channel支持异步IO操作。

java.nio.channels.AsynchronousServerSocketChannel

ServerSocket的aio版本,创建TCP服务端,绑定地址,监听端口等。

java.nio.channels.AsynchronousSocketChannel

面向流的异步socket channel,表示一个连接。

java.nio.channels.AsynchronousChannelGroup

异步channel的分组管理,目的是为了资源共享。一个AsynchronousChannelGroup绑定一个线程池,这个线程池执行两个任务:处理IO事件和派发CompletionHandler。AsynchronousServerSocketChannel创建的时候可以传入一个 AsynchronousChannelGroup,那么通过AsynchronousServerSocketChannel创建的 AsynchronousSocketChannel将同属于一个组,共享资源。

java.nio.channels.CompletionHandler

异步IO操作结果的回调接口,用于定义在IO操作完成后所作的回调工作。AIO的API允许两种方式来处理异步操作的结果:返回的Future模式或者注册CompletionHandler,我更推荐用CompletionHandler的方式,这些handler的调用是由 AsynchronousChannelGroup的线程池派发的。显然,线程池的大小是性能的关键因素。AsynchronousChannelGroup允许绑定不同的线程池,通过三个静态方法来创建:

public static AsynchronousChannelGroup withFixedThreadPool(int nThreads,
                                                              ThreadFactory threadFactory)
       throws IOException  

public static AsynchronousChannelGroup withCachedThreadPool(ExecutorService executor,
                                                               int initialSize)  

public static AsynchronousChannelGroup withThreadPool(ExecutorService executor)
       throws IOException 

需要根据具体应用相应调整,从框架角度出发,需要暴露这样的配置选项给用户。

在介绍完了aio引入的TCP的主要接口和类之后,我们来设想下一个aio框架应该怎么设计。参考非阻塞nio框架的设计,一般都是采用Reactor模式,Reacot负责事件的注册、select、事件的派发;相应地,异步IO有个Proactor模式,Proactor负责 CompletionHandler的派发,查看一个典型的IO写操作的流程来看两者的区别:

Reactor: send(msg) -> 消息队列是否为空,如果为空 -> 向Reactor注册OP_WRITE,然后返回 -> Reactor select -> 触发Writable,通知用户线程去处理 ->先注销Writable(很多人遇到的cpu 100%的问题就在于没有注销),处理Writeable,如果没有完全写入,继续注册OP_WRITE。注意到,写入的工作还是用户线程在处理。

Proactor: send(msg) -> 消息队列是否为空,如果为空,发起read异步调用,并注册CompletionHandler,然后返回。 -> 操作系统负责将你的消息写入,并返回结果(写入的字节数)给Proactor -> Proactor派发CompletionHandler。可见,写入的工作是操作系统在处理,无需用户线程参与。事实上在aio的API 中,AsynchronousChannelGroup就扮演了Proactor的角色。

CompletionHandler有三个方法,分别对应于处理成功、失败、被取消(通过返回的Future)情况下的回调处理:

public interface CompletionHandler<V,A> {  

     void completed(V result, A attachment);  

    void failed(Throwable exc, A attachment);  

    void cancelled(A attachment);
} 

若果想具体了解下NIO2 的使用那么请访问:点击打开链接

或者参考JDK7文档使用。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-07 03:25:25

深入理解JavaI/O流的相关文章

理解文档流

这个暑假里的实习是真正让我体会到了做前端一定要理解文档流.之前一直处于必须给元素加边框才能看到我讲元素放置在了文档中的什么位置.理解了文档流,脑海中就会定位到相应的元素. 文档流,其实本没有这样的定义,有的是普通流的概念.简单说就是元素按照其在HTML中的位置顺序决定排布的过程.并且这种过程遵循标准的描述. 即将窗体自上而下的分成一行一行,并在每行中按照从左至右的顺序排放,即为文档流.所有的HTML都在块框或者行内框中. 当浏览器开始渲染HTML文档,它从窗口的顶端开始,经过整个文档内容的过程中

从click事件理解DOM事件流

事件流是用来解释页面上的不同元素接受一个事件的顺序,首先要明确两点: 1.一个事件的影响元素可能不止一个(同心圆理论),但目标元素只有一个. 2.如果这些元素都绑定了相同名称的事件函数,我们怎么知道这些函数的运行顺序?于是有了事件流的概念(事件捕捉,事件冒泡) 举个例子: <div id="outer"> <p id="inner">Click me!</p> </div> 为了看起来方便,先无视CSS样式,那么蓝色的

深入理解javascript事件流

摘要:事件流这个东西是比较重要的,为了让自己更加理解js中的事件流,必须整理整理,梳理一下事件流的各种东西啊.本文大部分内容参考<javascript高级程序设计第三版> 先来一段书里的原文: 当浏览器发展到第四代时(IE4和Netscape Communicator 4),浏览器团队遇到一个很有意思的问题:页面的哪一部分会拥有特定的事件?想象下在一张纸上有一组同心圆,如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是一组圆.两家公司的开发团队在看待浏览器事件方面还是一致的.如果你单击

javaI/O流

 I/O流总结: (1)java流操作的类或接口 (2)按照不同的角度进行分类: 1〉按照流的方向可以分为输入流和输出流: 2〉按照处理数据的不同可以分为字节流(8位)和字符流(16位): (3)  J2SDK所提供的所有位于包java.io内的流类型都分别继承以下四种抽象流类型.                1〉InputStream基本方法: int read() //读取一个字节并且以一个整形返回,如果返回-1则说明已到输入流的末尾. int read(byte[] buff ) //读

重新认识Java-I/O流

记录一下在工作中遇到的一些有意思的问题,在之前系统学习过Java的IO流,但是在工作中长时间没有用到导致在这几天的一个需求中抓瞎了,查了好一会的资料耽误了一段时间,好了废话不多说,下面简单讲一下需求. 需求是这样的,数据库中有一张数据表的一个BLOB字段中存放了一个excel文件,我需要将这个文件放到服务器指定的文件夹下面.思路是这样的:1.在指定文件夹下面创建excel文件.2.jdbc取出数据库中的文件.3.各自转换为输入输出流将数据库中的文件数据导入到指定文件内.下面是代码: packag

【转】输入/输出流 - 深入理解Java中的流 (Stream)

基于流的数据读写,太抽象了,什么叫基于流,什么是流?Hadoop是Java语言写的,所以想理解好Hadoop的Streaming Data Access,还得从Java流机制入手.流机制也是JAVA及C++中的一个重要的机制,通过流使我们能够自由地操作包括文件,内存,IO设备等等中的数据. 首先,流是什么? 流是个抽象的概念,是对输入输出设备的抽象,Java程序中,对于数据的输入/输出操作都是以“流”的方式进行.设备可以是文件,网络,内存等. 流具有方向性,至于是输入流还是输出流则是一个相对的概

深入理解 Java中的 流 (Stream)

首先,流是什么? 流是个抽象的概念,是对输入输出设备的抽象,Java程序中,对于数据的输入/输出操作都是以"流"的方式进行.设备可以是文件,网络,内存等. 流具有方向性,至于是输入流还是输出流则是一个相对的概念,一般以程序为参考,如果数据的流向是程序至设备,我们成为输出流,反之我们称为输入流. 可以将流想象成一个"水流管道",水流就在这管道中形成了,自然就出现了方向的概念. 当程序需要从某个数据源读入数据的时候,就会开启一个输入流,数据源可以是文件.内存或网络等等.

[转]as3事件流机制彻底理解

题记: 看过网上一些as3事件流的教程,觉得大多都讲得不甚清楚,让人不能喝很直观的理解.而这篇教程以将事件流过程比喻成捕鱼过程,形象简单. 在此基础上对于as3事件流总算有了全面的理解.事件流机制说白了就是为了能让开发者能更好地控制事件调用顺序. addEventListener(type:String, listener:Function, useCapture:Boolean= false, priority:int= 0, useWeakReference:Boolean= false):

HTML 文档流和文本流的理解

文本流,概括地说其实就是一系列字符,是文档的读取和输出顺序,也就是我们通常看到的由左到右.由上而下的读取和输出形式,在网页中每个元素都是按照这个顺序进行排序和显示的,而position属性可以将元素从文本流脱离出来显示. 文档流,英文原版文档为"normal flow",翻译成常规流.普通流也就更好理解它了. 从直观上理解,常规流指的是元素按照其在 HTML 中的位置顺序决定排布的过程,主要的形式是自上而下(块级元素),一行接一行,每一行从左至右(行内元素). 定位类型包括三种: 常规