高并发之NIO、 AIO、BIO

来源:互联网 发布:mac电脑flash过期 编辑:程序博客网 时间:2024/06/08 18:31

1. 什么是NIO

NIO是New I/O的简称,与旧式的基于流的I/O方法相对,从名字看,它表示新的一套Java I/O标 准。它是在Java 1.4中被纳入到JDK中的,并具有以下特性: 

  • NIO是基于块(Block)的,它以块为基本单位处理数据 (硬盘上存储的单位也是按Block来存储,这样性能上比基于流的方式要好一些)
  • 为所有的原始类型提供(Buffer)缓存支持 
  • 增加通道(Channel)对象,作为新的原始 I/O 抽象
  • 支持锁(我们在平时使用时经常能看到会出现一些.lock的文件,这说明有线程正在使用这把锁,当线程释放锁时,会把这个文件删除掉,这样其他线程才能继续拿到这把锁)和内存映射文件的文件访问接口 
  • 提供了基于Selector的异步网络I/O 

Java NIO为什么是同步非阻塞的?

java中,同步就是说多线程去访问某个方法的时候,当访问到某些共享数据是同一时刻只能有一个线程去访问
阻塞就是说当前线程被挂起,例如调用了Thread.sleep() ;
Java NIO和IO的主要区别下表总结了Java NIO和IO之间的主要差别,我会更详细地描述表中每部分的差异。IO               NIO面向流             面向缓冲(块)同步阻塞IO      同步 非阻塞IO无                    选择器面向流与面向缓冲Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。阻塞与非阻塞IOJava IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此,一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

package entryNIO;import java.io.FileInputStream;import java.io.FileOutputStream;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;public class BufferAndChannel {public static void main(String[] args)  {String string="C:\\Users\\hp\\Desktop";long startTime=System.currentTimeMillis();nioCopyFile(string+"\\FileChannelImpl.java",string+"\\1.java");System.out.println("用时:"+(System.currentTimeMillis()-startTime)+"毫秒");System.out.println("读取完毕");}public static void nioCopyFile(String resource, String destination) {ByteBuffer buffer = null;//字节缓冲区try(//JDK 1.7 新特性  try-with-resource ,凡是实现了Closeable接口就可以用这种写法,而无需关闭流FileInputStream fis = new FileInputStream(resource);FileOutputStream fos = new FileOutputStream(destination);// Used by FileInputStream.getChannel() and RandomAccessFile.getChannel()FileChannel readChannel = fis.getChannel(); // 读文件通道FileChannel writeChannel = fos.getChannel(); // 写文件通道){buffer=ByteBuffer.allocate(1024*1024); // 为字节缓冲区分配空间int len=0;while ((len = readChannel.read(buffer))!=-1) {/*      反转此缓冲区。首先将限制设置为当前位置,然后将位置设置为 0。如果已定义了标记,则丢弃该标记。       在一系列通道读取或放置 操作之后,调用此方法为一系列通道写入或相对获取 操作做好准备。例如:       当将数据从一个地方传输到另一个地方时,经常将此方法与 compact 方法一起使用。 */buffer.flip();//反转此缓冲区writeChannel.write(buffer); // 写入文件buffer.clear();//清空缓冲区}} catch (Exception e) {e.printStackTrace();        }    }}
//理论上会比使用BufferedReader等快一点


package entryNIO;import java.nio.ByteBuffer;public class BufferAndChannel {public static void main(String[] args)  {long startTime=System.currentTimeMillis();try {Ha.main(args);} catch (Exception e) {e.printStackTrace();}System.out.println("用时:"+(System.currentTimeMillis()-startTime)+"毫秒");System.out.println("读取完毕");}}class Ha{public static void main(String[] args) throws Exception {ByteBuffer b = ByteBuffer.allocate(15); // 15个字节大小的缓冲区sop(b);//打印for (int i = 0; i < 10; i++) {// 存入10个字节数据b.put((byte) i);}sop(b);/*        该操作会重置position,通常,将buffer从写模式转换为读 模式时需要执行 flip()方法         该方法不仅重置了当前的position为0,还将limit设置到当前position的位置 。   limit的意义在于,来确定哪些数据是有意义的,换句话说,从position到limit之间的数据才是有意义的数据,        因为是上次操作的数据。所以flip操作往往是读写转换的意思。 */b.flip(); // 重置position,一般会重置为0sop(b);for (int i = 0; i < 5; i++) {System.out.print(b.get());}System.out.println();sop(b);b.flip();sop(b);}private static void sop(ByteBuffer b) {    //这里要区别下容量和上限,比如一个Buffer有15KB,那么15KB就是容量,我将5KB的文件读到Buffer中,那么上限就是5KB。System.out.println("limit=" + b.limit() + " capacity=" + b.capacity()+ " position=" + b.position());}}


将文件映射到内存

package entryNIO;import java.io.RandomAccessFile;import java.nio.MappedByteBuffer;import java.nio.channels.FileChannel;public class BufferAndChannel {public static void main(String[] args) {long startTime = System.currentTimeMillis();try (RandomAccessFile raf = new RandomAccessFile("C:\\FileChannelImpl.java", "rw");  FileChannel fc = raf.getChannel();) {// 将文件映射到内存中MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, raf.length());//byte[] byteArray=new byte[1024*1024];while (mbb.hasRemaining()) {// between the current position and the limit.在这范围内是否还有System.out.print((char) mbb.get());//mbb.get(dst, offset, length)}mbb.put(0, (byte) 98); // 修改文件} catch (Exception e) {e.printStackTrace();}System.out.println("用时:" + (System.currentTimeMillis() - startTime) + "毫秒");System.out.println("读取完毕");}}


原创粉丝点击