探索Java NIO的历程
前段时间有些时间,打算看看NIO的东西,本来以为很快可以了解的东西,却用了很多时间。
首先Goole NIO可以看到很多的教程,非阻塞,Buffer,内存映射,块读取前三个很快就有所了解
尝试着写了些小程序,学习东西的时候总喜欢写点小例子。
唯独块读取没有找到对应的东西。(在过程中,主要看了IBM 的NIO入门)
		首先,IBM NIO入门中的语句
--------------------------------------------------------------------------------
原来的 I/O 库(在 java.io.*中) 与 NIO 最重要的区别是数据打包和传输的方式。正如前面提到的,
原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。 面向流 的 I/O 系统一次一个字节地处
理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。 一个 面向块 的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的 I/O 缺少一些面向流的I/O 所具有的优雅性和简单性。 
--------------------------------------------------------------------------------
首先简单的印象是NIO快,所以想写个程序验证一下.如下复制:
		 
		
				 1
				 
				 public
				 
				static
				 
				void
				 test2(String name1, String name2)
				public
				 
				static
				 
				void
				 test2(String name1, String name2) 
				
						 {
				
				
						{
						 2
						
								 long
						 start 
						=
						 System.currentTimeMillis();
        
						long
						 start 
						=
						 System.currentTimeMillis();
						 3
						
								 
								 try
        
						try
						 
						
								 {
						
						
								{
								 4
								
										 FileInputStream fis 
								=
								 
								new
								 FileInputStream(name1);
            FileInputStream fis 
								=
								 
								new
								 FileInputStream(name1);
								 5
								
										 FileOutputStream fos 
								=
								 
								new
								 FileOutputStream(name2);
            FileOutputStream fos 
								=
								 
								new
								 FileOutputStream(name2);
								 6
								
										 byte
								[] buf 
								=
								 
								new
								 
								byte
								[
								8129
								];
            
								byte
								[] buf 
								=
								 
								new
								 
								byte
								[
								8129
								];
								 7
								
										 
										 while
								 (
								true
								)
            
								while
								 (
								true
								) 
								
										 {
								
								
										{                
										 8
										
												 int
										 n 
										=
										 fis.read(buf);
                
										int
										 n 
										=
										 fis.read(buf);
										 9
										
												 
												 if
										 (n 
										==
										 
										-
										1
										)
                
										if
										 (n 
										==
										 
										-
										1
										) 
										
												 {
										
										
												{
												10
												
														 break
												;
                    
												break
												;
												11
												
														 }
                }
										
										
												
										
										12
										
												 fos.write(buf,
										0
										,n);
                fos.write(buf,
										0
										,n);
										13
										
												 }
            }
								
								
										
								
								14
								
										 fis.close();
            fis.close();
								15
								
										 fos.close();
            fos.close();
								16
								
										 
										 }
						
						 
						catch
						 (Exception e)
        }
						
						 
						catch
						 (Exception e) 
						
								 {
						
						
								{
								17
								
										 e.printStackTrace();
            e.printStackTrace();
								18
								
										 }
        }
						
						
								
						
						19
						
								 long
						 end 
						=
						 System.currentTimeMillis();
        
						long
						 end 
						=
						 System.currentTimeMillis();
						20
						
								 long
						 time 
						=
						 end 
						-
						 start;
        
						long
						 time 
						=
						 end 
						-
						 start;
						21
						
								 System.out.println(time);
        System.out.println(time);
						22
						
								 }
    }
				
				
						
				
				23
				
						 
    
				24
				
						 
						 public
				 
				static
				 
				void
				 test3(String name1, String name2)
    
				public
				 
				static
				 
				void
				 test3(String name1, String name2) 
				
						 {
				
				
						{
						25
						
								 long
						 start 
						=
						 System.currentTimeMillis();
        
						long
						 start 
						=
						 System.currentTimeMillis();
						26
						
								 
								 try
        
						try
						 
						
								 {
						
						
								{
								27
								
										 FileInputStream in 
								=
								 
								new
								 FileInputStream(name1);
            FileInputStream in 
								=
								 
								new
								 FileInputStream(name1);
								28
								
										 FileOutputStream out 
								=
								 
								new
								 FileOutputStream(name2);
            FileOutputStream out 
								=
								 
								new
								 FileOutputStream(name2);
								29
								
										 FileChannel fc1 
								=
								 in.getChannel();
            FileChannel fc1 
								=
								 in.getChannel();
								30
								
										 FileChannel fc2 
								=
								 out.getChannel();
            FileChannel fc2 
								=
								 out.getChannel();
								31
								
										 ByteBuffer bb 
								=
								 ByteBuffer.allocate(
								8129
								);
            ByteBuffer bb 
								=
								 ByteBuffer.allocate(
								8129
								);
								32
								
										 
										 while
								 (
								true
								)
            
								while
								 (
								true
								) 
								
										 {
								
								
										{
										33
										
												 bb.clear();
                bb.clear();
										34
										
												 int
										 n 
										=
										 fc1.read(bb);
                
										int
										 n 
										=
										 fc1.read(bb);
										35
										
												 
												 if
										 (n 
										==
										 
										-
										1
										)
                
										if
										 (n 
										==
										 
										-
										1
										) 
										
												 {
										
										
												{
												36
												
														 break
												;
                    
												break
												;
												37
												
														 }
                }
										
										
												
										
										38
										
												 bb.flip();
                bb.flip();
										39
										
												 fc2.write(bb);
                fc2.write(bb);
										40
										
												 }
            }
								
								
										
								
								41
								
										 fc1.close();
            fc1.close();
								42
								
										 fc2.close();
            fc2.close();
								43
								
										 
										 }
						
						 
						catch
						 (IOException e)
        }
						
						 
						catch
						 (IOException e) 
						
								 {
						
						
								{
								44
								
										 
										
								
								45
								
										 }
        }
						
						
								
						
						46
						
								 long
						 end 
						=
						 System.currentTimeMillis();
        
						long
						 end 
						=
						 System.currentTimeMillis();
						47
						
								 long
						 time 
						=
						 end 
						-
						 start;
        
						long
						 time 
						=
						 end 
						-
						 start;
						48
						
								 System.out.println(time);
        System.out.println(time);
						49
						
								 }
    }
				
		 
		本以为可以结束,结果测试结果出乎意料,函数一比函数二要快,就是说Old IO快于NIO ,从此
 也就开始了整个过程:
 为了了解这个问题,仔细搜索并仔细再看IBM 的NIO教程,看到如下这段话
 ---------------------------------------------
 在 JDK 1.4 中原来的 I/O 包和 NIO 已经很好地集成了。 java.io.* 已经以 NIO 为基础重新实现了,
 所以现在它可以利用 NIO 的一些特性。例如, java.io.* 包中的一些类包含以块的形式读写数据的方法,
 这使得即使在更面向流的系统中,处理速度也会更快。 也可以用 NIO 库实现标准 I/O 功能。例如,
 可以容易地使用块 I/O 一次一个字节地移动数据。但是正如您会看到的,NIO 还提供了原 I/O 包中所没有的许多好处。 
    ---------------------------------------------
    所以我想,是否因为InputStream中使用了块读取实现了呢,所以进入JDK1.4中的InputStream中
    看看source,首先引起我注意的是read函数,当参数是一个byte数组的时候,直接调用的native实现
    难道是这个,为了验证,下载了一个JDK1.3下来,发现JDK1.3是一样的。
    
    继续,我想是否是JVM底层实现了块读取呢,为了证明这个我用JDK1.3和JDK1.4同时实现了类似的函数,    测试的结果再次出乎意料,性能相差不大.那就不是这个了。
  为此多方查找资料,未果,为此多写几个函数,好好测试一下IO的不同。于是有了如下的一些函数
		
				  1
				 //
				 exec
				//
				 exec
				
						
				
				  2
				
						 
						 public
				 
				static
				 
				void
				 test1(String name1, String name2)
				
				    
				public
				 
				static
				 
				void
				 test1(String name1, String name2) 
				
						 {
				
				
						{
						  3
						
								 long
						 start 
						=
						 System.currentTimeMillis();
        
						long
						 start 
						=
						 System.currentTimeMillis();
						  4
						
								 
								 try
        
						try
						 
						
								 {
						
						
								{
								  5
								
										 String cmd 
								=
								 
								"
								cmd /c copy d:\\out1.txt d:\\out2.txt
								"
								;
            String cmd 
								=
								 
								"
								cmd /c copy d:\\out1.txt d:\\out2.txt
								"
								;
								  6
								
										 System.out.println(cmd);
            System.out.println(cmd);
								  7
								
										 Process p 
								=
								 Runtime.getRuntime().exec(cmd);÷
            Process p 
								=
								 Runtime.getRuntime().exec(cmd);÷
								  8
								
										 p.waitFor();
            p.waitFor();
								  9
								
										 
										 }
						
						 
						catch
						 (Exception e)
        }
						
						 
						catch
						 (Exception e) 
						
								 {
						
						
								{
								 10
								
										 e.printStackTrace();
            e.printStackTrace();
								 11
								
										 }
        }
						
						
								
						
						 12
						
								 long
						 end 
						=
						 System.currentTimeMillis();
        
						long
						 end 
						=
						 System.currentTimeMillis();
						 13
						
								 long
						 time 
						=
						 end 
						-
						 start;
        
						long
						 time 
						=
						 end 
						-
						 start;
						 14
						
								 System.out.println(time);
        System.out.println(time);
						 15
						
								 }
    }
				
				
						
				
				 16
				
						 
						
				
				 17
				
						 //
				 old io
    
				//
				 old io
				
						
				
				 18
				
						 
						 public
				 
				static
				 
				void
				 test2(String name1, String name2)
				
				    
				public
				 
				static
				 
				void
				 test2(String name1, String name2) 
				
						 {
				
				
						{
						 19
						
								 long
						 start 
						=
						 System.currentTimeMillis();
        
						long
						 start 
						=
						 System.currentTimeMillis();
						 20
						
								 
								 try
        
						try
						 
						
								 {
						
						
								{
								 21
								
										 FileInputStream fis 
								=
								 
								new
								 FileInputStream(name1);
            FileInputStream fis 
								=
								 
								new
								 FileInputStream(name1);
								 22
								
										 FileOutputStream fos 
								=
								 
								new
								 FileOutputStream(name2);
            FileOutputStream fos 
								=
								 
								new
								 FileOutputStream(name2);
								 23
								
										 
										 while
								 (
								true
								)
            
								while
								 (
								true
								) 
								
										 {
								
								
										{
										 24
										
												 byte
										[] buf 
										=
										 
										new
										 
										byte
										[
										8129
										];
                
										byte
										[] buf 
										=
										 
										new
										 
										byte
										[
										8129
										];
										 25
										
												 int
										 n 
										=
										 fis.read(buf);
                
										int
										 n 
										=
										 fis.read(buf);
										 26
										
												 
												 if
										 (n 
										==
										 
										-
										1
										)
                
										if
										 (n 
										==
										 
										-
										1
										) 
										
												 {
										
										
												{
												 27
												
														 break
												;
                    
												break
												;
												 28
												
														 }
                }
										
										
												
										
										 29
										
												 fos.write(buf);
                fos.write(buf);
										 30
										
												 }
            }
								
								
										
								
								 31
								
										 fis.close();
            fis.close();
								 32
								
										 fos.close();
            fos.close();
								 33
								
										 
										 }
						
						 
						catch
						 (Exception e)
        }
						
						 
						catch
						 (Exception e) 
						
								 {
						
						
								{
								 34
								
										 e.printStackTrace();
            e.printStackTrace();
								 35
								
										 }
        }
						
						
								
						
						 36
						
								 long
						 end 
						=
						 System.currentTimeMillis();
        
						long
						 end 
						=
						 System.currentTimeMillis();
						 37
						
								 long
						 time 
						=
						 end 
						-
						 start;
        
						long
						 time 
						=
						 end 
						-
						 start;
						 38
						
								 System.out.println(time);
        System.out.println(time);
						 39
						
								 }
    }
				
				
						
				
				 40
				
						 
						
				
				 41
				
						 //
				 new io
    
				//
				 new io
				
						
				
				 42
				
						 
						 public
				 
				static
				 
				void
				 test3(String name1, String name2)
				
				    
				public
				 
				static
				 
				void
				 test3(String name1, String name2) 
				
						 {
				
				
						{
						 43
						
								 long
						 start 
						=
						 System.currentTimeMillis();
        
						long
						 start 
						=
						 System.currentTimeMillis();
						 44
						
								 
								 try
        
						try
						 
						
								 {
						
						
								{
								 45
								
										 FileInputStream in 
								=
								 
								new
								 FileInputStream(name1);
            FileInputStream in 
								=
								 
								new
								 FileInputStream(name1);
								 46
								
										 FileOutputStream out 
								=
								 
								new
								 FileOutputStream(name2);
            FileOutputStream out 
								=
								 
								new
								 FileOutputStream(name2);
								 47
								
										 FileChannel fc1 
								=
								 in.getChannel();
            FileChannel fc1 
								=
								 in.getChannel();
								 48
								
										 FileChannel fc2 
								=
								 out.getChannel();
            FileChannel fc2 
								=
								 out.getChannel();
								 49
								
										 ByteBuffer bb 
								=
								 ByteBuffer.allocate(
								8129
								);
            ByteBuffer bb 
								=
								 ByteBuffer.allocate(
								8129
								);
								 50
								
										 
										 while
								 (
								true
								)
            
								while
								 (
								true
								) 
								
										 {
								
								
										{
										 51
										
												 bb.clear();
                bb.clear();
										 52
										
												 int
										 n 
										=
										 fc1.read(bb);
                
										int
										 n 
										=
										 fc1.read(bb);
										 53
										
												 
												 if
										 (n 
										==
										 
										-
										1
										)
                
										if
										 (n 
										==
										 
										-
										1
										) 
										
												 {
										
										
												{
												 54
												
														 break
												;
                    
												break
												;
												 55
												
														 }
                }
										
										
												
										
										 56
										
												 bb.flip();
                bb.flip();
										 57
										
												 fc2.write(bb);
                fc2.write(bb);
										 58
										
												 }
            }
								
								
										
								
								 59
								
										 fc1.close();
            fc1.close();
								 60
								
										 fc2.close();
            fc2.close();
								 61
								
										 
										 }
						
						 
						catch
						 (IOException e)
        }
						
						 
						catch
						 (IOException e) 
						
								 {
						
						
								{
								 62
								
										 
										
								
								 63
								
										 }
        }
						
						
								
						
						 64
						
								 long
						 end 
						=
						 System.currentTimeMillis();
        
						long
						 end 
						=
						 System.currentTimeMillis();
						 65
						
								 long
						 time 
						=
						 end 
						-
						 start;
        
						long
						 time 
						=
						 end 
						-
						 start;
						 66
						
								 System.out.println(time);
        System.out.println(time);
						 67
						
								 }
    }
				
				
						
				
				 68
				
						 
						
				
				 69
				
						 //
				 fast copy
    
				//
				 fast copy
				
						
				
				 70
				
						 
						 public
				 
				static
				 
				void
				 test4(String name1, String name2)
				
				    
				public
				 
				static
				 
				void
				 test4(String name1, String name2) 
				
						 {
				
				
						{
						 71
						
								 long
						 start 
						=
						 System.currentTimeMillis();
        
						long
						 start 
						=
						 System.currentTimeMillis();
						 72
						
								 
								 try
        
						try
						 
						
								 {
						
						
								{
								 73
								
										 FileInputStream in 
								=
								 
								new
								 FileInputStream(name1);
            FileInputStream in 
								=
								 
								new
								 FileInputStream(name1);
								 74
								
										 ;
            ;
								 75
								
										 FileOutputStream out 
								=
								 
								new
								 FileOutputStream(name2);
            FileOutputStream out 
								=
								 
								new
								 FileOutputStream(name2);
								 76
								
										 ;
            ;
								 77
								
										 FileChannel fc1 
								=
								 in.getChannel();
            FileChannel fc1 
								=
								 in.getChannel();
								 78
								
										 FileChannel fc2 
								=
								 out.getChannel();
            FileChannel fc2 
								=
								 out.getChannel();
								 79
								
										 ByteBuffer bb 
								=
								 ByteBuffer.allocateDirect(
								8129
								);
            ByteBuffer bb 
								=
								 ByteBuffer.allocateDirect(
								8129
								);
								 80
								
										 
										 while
								 (
								true
								)
            
								while
								 (
								true
								) 
								
										 {
								
								
										{
										 81
										
												 bb.clear();
                bb.clear();
										 82
										
												 int
										 n 
										=
										 fc1.read(bb);
                
										int
										 n 
										=
										 fc1.read(bb);
										 83
										
												 
												 if
										 (n 
										==
										 
										-
										1
										)
                
										if
										 (n 
										==
										 
										-
										1
										) 
										
												 {
										
										
												{
												 84
												
														 break
												;
                    
												break
												;
												 85
												
														 }
                }
										
										
												
										
										 86
										
												 bb.flip();
                bb.flip();
										 87
										
												 fc2.write(bb);
                fc2.write(bb);
										 88
										
												 }
            }
								
								
										
								
								 89
								
										 fc1.close();
            fc1.close();
								 90
								
										 fc2.close();
            fc2.close();
								 91
								
										 
										 }
						
						 
						catch
						 (IOException e)
        }
						
						 
						catch
						 (IOException e) 
						
								 {
						
						
								{
								 92
								
										 
										
								
								 93
								
										 }
        }
						
						
								
						
						 94
						
								 long
						 end 
						=
						 System.currentTimeMillis();
        
						long
						 end 
						=
						 System.currentTimeMillis();
						 95
						
								 long
						 time 
						=
						 end 
						-
						 start;
        
						long
						 time 
						=
						 end 
						-
						 start;
						 96
						
								 System.out.println(time);
        System.out.println(time);
						 97
						
								 }
    }
				
				
						
				
				 98
				
						 
						
				
				 99
				
						 //
				 transfer ,read and write at same time
    
				//
				 transfer ,read and write at same time
				
						
				
				100
				
						 
						 public
				 
				static
				 
				void
				 test5(String name1, String name2)
				
				    
				public
				 
				static
				 
				void
				 test5(String name1, String name2) 
				
						 {
				
				
						{
						101
						
								 long
						 start 
						=
						 System.currentTimeMillis();
        
						long
						 start 
						=
						 System.currentTimeMillis();
						102
						
								 
								 try
        
						try
						 
						
								 {
						
						
								{
								103
								
										 RandomAccessFile raf1 
								=
								 
								new
								 RandomAccessFile(name1, 
								"
								rw
								"
								);
            RandomAccessFile raf1 
								=
								 
								new
								 RandomAccessFile(name1, 
								"
								rw
								"
								);
								104
								
										 RandomAccessFile raf2 
								=
								 
								new
								 RandomAccessFile(name2, 
								"
								rw
								"
								);
            RandomAccessFile raf2 
								=
								 
								new
								 RandomAccessFile(name2, 
								"
								rw
								"
								);
								105
								
										 FileChannel fc1 
								=
								 raf1.getChannel();
            FileChannel fc1 
								=
								 raf1.getChannel();
								106
								
										 FileChannel fc2 
								=
								 raf2.getChannel();
            FileChannel fc2 
								=
								 raf2.getChannel();
								107
								
										 fc1.transferTo(
								0
								, raf1.length(), fc2);
            fc1.transferTo(
								0
								, raf1.length(), fc2);
								108
								
										 fc1.close();
            fc1.close();
								109
								
										 fc2.close();
            fc2.close();
								110
								
										 
										 }
						
						 
						catch
						 (Exception e)
        }
						
						 
						catch
						 (Exception e) 
						
								 {
						
						
								{
								111
								
										 e.printStackTrace();
            e.printStackTrace();
								112
								
										 }
        }
						
						
								
						
						113
						
								 long
						 end 
						=
						 System.currentTimeMillis();
        
						long
						 end 
						=
						 System.currentTimeMillis();
						114
						
								 long
						 time 
						=
						 end 
						-
						 start;
        
						long
						 time 
						=
						 end 
						-
						 start;
						115
						
								 System.out.println(time);
        System.out.println(time);
						116
						
								 }
    }
				
		 
		首先测试的文件是一个30几M的文件,测试结果出乎意料,是否因为文件太小呢 ?用个200M的文件再次
 测试一下,结果更匪夷所思,transfor的方式快些,exec的方式快些,Old IO和NIO相差不大,而且
 稍快与NIO,拿到这个结果真让人气馁,再次查找资料,但都没有解决自己的疑问,这个时候看到了
 Think In Java第三版上讲到NIO,更让我兴奋的是我看到如下的话,大概意思吧,原文记不住了
 "Java NIO在文件读取和网络方面有很大的性能提高,网络方面不说,这里说说文件操作"看到这段话
 真是高兴,因为Think In Java一向都是有很多写的很好的小例子,怀着这种心情,往下看,终于找到了
 一个显示性能差别小例子,但让我哭笑不得的是,作者居然是使用的内存映射的例子。类似这样
 RandomAccessFile raf1 = new RandomAccessFile(name1, "rw");
   FileChannel fc1 = raf1.getChannel();
   MappedByteBuffer mbb = fc1.map(FileChannel.MapMode.READ_WRITE, 0,
     1024);
 使用内存映射来和传统的IO来对比读写,本来就是个不公平的事情。内存映射的读取实际是内存读写
 和传统IO比肯定有很大差距。到现在,从开始NIO到现在已经有1周的时间了,每天我都会在工作之余拿出 1-2个小时看看NIO,但这个问题越来越迷离。 
 今天有时间把这个过程写出来,一方面感觉过程无奈,写出来留个纪念,另一方面希望和大家交流一下, 如果你没看过NIO或者没有作过这个方面的尝试,如果你有兴趣可以一起探讨,如果你了解这个问题,请 不吝赐教,或给个提示,或者告诉我我的测试错在什么地方,不胜感谢。同时,我会继续这个问题,直到 找到答案。然后会把答案放上来共享。