前文中咱们总结了linux系统中的5中IO模型,而且着重介绍了其中的4种IO模型:html
可是前面总结的IO模型只是限定在linux下,更偏向于操做系统底层的概念,并无涉及到Java应用层面,其实Java中也提供了和前面操做系统层面的IO模型相对应的概念,这是本文接下来要讲的重点。java
一样本文会围绕以下几点进行展开:linux
适用场景github
总结服务器
传统Java IO提供的面向流的IO操做方式就属于阻塞式的,调用其read()或write()方法的线程会阻塞,直到完成了数据的读写,在读写的过程当中线程是什么都作不了的。架构
Java NIO类库提供了多种支持非阻塞模式的类,好比Channel、Buffer,能够将其设置为非阻塞模式,线程向channel请求读数据时,只会获取已经就绪的数据,并不会阻塞以等待全部数据都准备好,这样在数据准备的阶段线程就可以去处理别的事情,这就是非阻塞式读,对于写数据是同样的。并发
这里和上面阻塞的区别就是,调用read()或write()方法并不阻塞,而是会当即返回,可是这时候IO操做每每是尚未结束的。dom
Java NIO中的Selector容许单个线程监控多个channel,能够将多个channel注册到一个Selector中,而后能够"select"出已经准备好数据的channel,或者准备好写入的channel,而后对其进行读或者写数据,这就是多路复用。
异步IO模型是比较理想的IO模型,在异步IO模型中,当用户线程发起read操做以后,马上就能够开始去作其它的事。另外一方面,内核会等待数据准备完成,而后将数据复制到用户线程,当这一切都完成以后,内核会给用户线程发送一个信号,告诉它read操做完成了。也就是说用户线程彻底不须要关心实际的整个IO操做了,只须要发起请求就好了,当收到内核的成功信号时就能够直接去使用数据了。这就是和非阻塞式的区别,若是说阻塞式IO是彻底手动,非阻塞式IO就是半自动,而异步IO就是全自动,多路复用呢?我以为能够是半自动冲锋枪^_^
在Java 7中,提供了Asynchronous IO,Java NIO中的AsynchronousFileChannel支持异步模型实现的。
BIO方式适用于链接数目比较小且每一个链接占用大量宽带,这种方式对服务器资源要求比较高,JDK1.4之前的惟一选择,但程序直观简单易理解。
NIO方式适用于链接数目多且链接比较短(轻操做)的架构,好比聊天服务器,编程比较复杂,JDK1.4开始支持。
AIO方式适用于链接数目多且链接比较长(重操做)的架构,好比相册服务器,充分调用OS参与并发操做,编程比较复杂,JDK7开始支持。
前面讲了这么多,即讲了linux下的IO模型,又讲了Java中对这些IO模型的支持,到这里我以为是时候找一些Java中实际的例子看看,下面就分别用三种IO模型来读写文件。
public void rwByBIO() { BufferedReader br = null; FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("test.txt"); out = new FileOutputStream("testBIO.txt"); List<Integer> list = new ArrayList(); int temp; while((temp = in.read()) != -1) { out.write(temp); } br = new BufferedReader(new InputStreamReader(new FileInputStream("testBIO.txt"))); System.out.println(br.readLine()); }catch(Exception e) { e.printStackTrace(); }finally { if(br != null) { try { br.close(); }catch(IOException e) { e.printStackTrace(); } } if(out != null) { try { out.close(); }catch(IOException e) { e.printStackTrace(); } } } }
在根目录下准备好文件test.txt,里面写上准备好的内容,好比"黄沙百战穿金甲,不破楼兰终不还",而后跑起来,以后应该会多出一个文件testBIO.txt,里面内容是同样的。咱们经过BIO的方式读取test.txt中的内容,一样以BIO的方式写入到testBIO.txt中。
public void rwByNIO() { FileChannel readChannel = null; FileChannel writeChannel = null; try { readChannel = new RandomAccessFile(new File("test.txt"),"r").getChannel(); writeChannel = new RandomAccessFile(new File("testNIO.txt"),"rw").getChannel(); ByteBuffer buffer = ByteBuffer.allocate(10); int bytesRead = readChannel.read(buffer); while(bytesRead != -1) { buffer.flip(); while(buffer.hasRemaining()) { // 写入文件 writeChannel.write(buffer); } // 一次写完以后 buffer.clear(); bytesRead = readChannel.read(buffer); } }catch(Exception e) { e.printStackTrace(); }finally { if(readChannel != null) { try { readChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if(writeChannel != null) { try { writeChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } }
这里是经过NIO中的FileChannel来读写文件,可是要注意,虽然这一节的标题是说用NIO的方式来读写文件,可是FileChannel并不支持非阻塞模式,因此其实际上仍是属于阻塞的,即BIO的方式,只是由于这里为了统一演示读写文件的例子,因此仍然使用NIO中的FileChannel类来完成。
public void rwByAIO() { Path path = Paths.get("test.txt"); AsynchronousFileChannel fileChannel = null; try { fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ); ByteBuffer buffer = ByteBuffer.allocate(1024); long position = 0; Future<Integer> operation = fileChannel.read(buffer, position); while(!operation.isDone()); buffer.flip(); Path writePath = Paths.get("testAIO.txt"); if(!Files.exists(writePath)){ Files.createFile(writePath); } AsynchronousFileChannel writeFileChannel = AsynchronousFileChannel.open(writePath, StandardOpenOption.WRITE); writeFileChannel.write(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(Integer result, ByteBuffer attachment) { System.out.println("bytes written: " + result); } @Override public void failed(Throwable exc, ByteBuffer attachment) { System.out.println("Write failed"); exc.printStackTrace(); } }); }catch(Exception e) { e.printStackTrace(); } }
这个例子中是经过异步地方式来读写文件。当调用了Java NIO中的AsynchronousFileChannel对这种操做提供了支持,当调用其read()方法时会当即返回一个Future对象,经过调用其isDone方法来得知数据是否读取完毕。
本文结合前文讲到的IO模型,分别对应到Java中的具体类库实现,并经过例子演示了BIO、NIO、AIO三种方式读写文件。