a.面向流的,InputStream(),OuputStream字節輸入流,字節輸出流,Reader,Writer字符輸入流,字符輸出流
b.阻塞的IO,好比Socket,它的底層用的BIO機制,accept()、connect()、write()調用時會產生阻塞。阻塞模型的侷限性:不可能應對高併發、搞訪問量的場景html
總結:BIO方式適用於鏈接數目比較小而且一次發送大量數據的場景,這種方式對服務器資源要求比較高,併發侷限於應用中java
很簡單轉換成圖型說明就是web瀏覽器發一個請求過來,web服務器就要new 一個線程來處理這個請求,這是傳統的請求處理模型,這也就引來一個很大的問題,當請求越多,服務器端的啓用線程也要越多,咱們都知道linux(window)的文件句柄數有是限的,默認是1024,固然能夠修改,上限好像是65536 ,(一個柄也至關於一個socket也至關於一個thread,linux查看文件句柄Unlimit -a) 其實在實際當中只要併發到1000上下響應請求就會很慢了,因此這種模型是有問題的,這種也就是同步阻塞IO編程(JAVA BIO)
linux
如下是簡單的bio例子:web
Server端代碼:編程
import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; /** * * @author * @date 2019-02-27 */ public class Server { public static void main(String[] args) throws IOException { ServerSocket socket = new ServerSocket(); socket.bind(new InetSocketAddress(9999)); while(true) { Socket sc = socket.accept(); new Thread(new Accept(sc)).start(); } } } class Accept implements Runnable{ private Socket sc = null; public Accept(Socket sc) { sc = this.sc; } @Override public void run() { System.out.println("提供服務的線程id是:"+Thread.currentThread().getId()); } }
Clinet端代碼數組
/** * * @author * @date 2019-02-27 */ public class Client { public static void main(String[] args) throws IOException { Socket sc = new Socket(); sc.connect(new InetSocketAddress( "127.0.0.1",9999),0); while(true) { } } }
啓動server端,而後屢次啓動client端,打印結果以下
瀏覽器
a.面向緩衝區的(Buffer),NIO是經過緩衝區去操做數據的,能夠用緩衝區靈活操做數據,此外,NIO是面向通道的(Channel),在通道上,便可以讀數據,也能夠寫數據服務器
b.NIO是非阻塞的IO,能夠利用NIO處理高併發和高訪問量的場景,NIO適合處理鏈接數目特別多,可是鏈接比較短(輕操做)的場景,Jetty,Mina,ZooKeeper等都是基於java的nio實現。服務器須要支持超大量的長時間鏈接。好比10000個鏈接以上,而且每一個客戶端並不會頻繁地發送太多數據網絡
首先,引入nio的3個相關概念:
1> Buffer 緩衝區
難用的buffer是一個抽象的對象,下面還有ByteBuffer,IntBuffer,LongBuffer等子類,相比老的IO將數據直接讀/寫到Stream對象,NIO是將全部數據都用到緩衝區處理,它本質上是一個數組,提供了位置,容量,上限等操做方法,仍是直接看代碼代碼來得直接
2>Channel 通道
如自來水管同樣,支持網絡數據從Channel中讀寫,通道寫流最大不一樣是通道是雙向的,而流是一個方向上移動(InputStream/OutputStream),通道可用於讀/寫或讀寫同時進行,它還能夠和下面要講的selector結合起來,有多種狀態位,方便selector去識別. 通道分兩類,一:網絡讀寫(selectableChannel),另外一類是文件操做(FileChannel),咱們經常使用的是上面例子中的網絡讀寫!併發
3>Selector 多路複用選擇器
它是神同樣存在的東西,多路複用選擇器提供選擇已經就緒的任務的能力,也就是selector會不斷輪詢註冊在其上的通道(Channel),若是某個通道發生了讀寫操做,這個通道處於就緒狀態,會被selector輪詢出來,而後經過selectionKey能夠取得就緒的Channel集合,從而進行後續的IO操做.
一個多路複用器(Selector)能夠負責成千上萬個Channel,沒有上限,這也是JDK使用epoll代替了傳統的selector實現,得到鏈接句柄沒有限制.這也意味着咱們只要一個線程負責selector的輪詢,就能夠接入成千上萬個客戶端,這是JDK,NIO庫的巨大進步.

如下是server端代碼:
import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class NioServer { public static void main(String[] args) throws Exception { ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.configureBlocking(false); ssc.bind(new InetSocketAddress(6666)); Selector selector = Selector.open(); ssc.register(selector,SelectionKey.OP_ACCEPT); while(true) { selector.select(); Set<SelectionKey> set = selector.selectedKeys(); //獲取事件結合的迭代器 Iterator<SelectionKey> iter = set.iterator(); while(iter.hasNext()) { SelectionKey sk = iter.next(); if(sk.isAcceptable()) { //證實有客戶端請求創建回遷鏈接 ServerSocketChannel ss = (ServerSocketChannel)sk.channel(); //建立會話對象 SocketChannel sc = null; while(sc==null) { sc = ss.accept(); } //設置爲非阻塞模式 sc.configureBlocking(false); //輸出爲客戶端提供服務的線程ID System.out.println("有客戶端連入,負責提供服務的" + "線程id:"+Thread.currentThread().getId()); //爲sc註冊read事件和write事件 sc.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE); } if(sk.isReadable()) { //獲取SocketChannel對象 SocketChannel sc = (SocketChannel)sk.channel(); ByteBuffer buf = ByteBuffer.allocate(10); sc.read(buf); System.out.println("服務器端獨到的內容:" +new String(buf.array())); System.out.println("負責處理讀請求的線程ID:" +Thread.currentThread().getId()); sc.register(selector,sk.interestOps()&~SelectionKey.OP_READ); } if(sk.isWritable()) { //獲取SocketChannel對象 SocketChannel sc = (SocketChannel)sk.channel(); ByteBuffer buf = ByteBuffer.wrap("over".getBytes()); sc.write(buf); System.out.println("負責處理寫請求的線程ID:" +Thread.currentThread().getId()); sc.register(selector,sk.interestOps()&~SelectionKey.OP_WRITE); } //刪除該事件,目的:爲了防止同一個事件被處理屢次 iter.remove(); } } } }
如下是client端代碼
import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; public class NIOClient { public static void main(String[] args) throws Exception { SocketChannel sc = SocketChannel.open(); sc.configureBlocking(false); sc.connect(new InetSocketAddress("127.0.0.1", 6666)); while(!sc.isConnected()) { sc.finishConnect(); } ByteBuffer buf = ByteBuffer.wrap("helloworld".getBytes()); sc.write(buf); ByteBuffer readBuf = ByteBuffer.allocate(9); Thread.sleep(1000); sc.read(readBuf); System.out.println("客戶端讀取到服務器發送過來的內容:" +new String(readBuf.array())); while(true) { } } }
這裏補充一點aio,這個比NIO先進的技術,最終實現了
netty
這是神同樣存在的java nio框架, 這個偏底層的東西,可能你接觸較少卻又無處不在,好比:

在業界有一篇沒法超越的netty入門文章,我也沒這個能力超越,只能雙手奉上,大家好好研讀,必然學有所成!
http://ifeve.com/netty5-user-guide/