<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          NIO之緩沖區(qū)詳解

          共 10703字,需瀏覽 22分鐘

           ·

          2022-06-27 08:59

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

          作者 | 汪偉俊 

          出品 | Java技術(shù)迷(ID:JavaFans1024)

          NIO是JDK1.4新加入的API,很多人把它稱為Not Blocking IO,意為非阻塞式IO,它是相較于傳統(tǒng)的IO方式而言的,NIO在數(shù)據(jù)打包和傳輸方式上進(jìn)行了較大的改良,增加了緩沖區(qū)、通道等等內(nèi)容,使得NIO能夠借助一個(gè)線程處理多個(gè)資源,讀寫效率也大大提升。

          先來(lái)介紹NIO中的一個(gè)基本概念,緩沖區(qū)。在NIO中,任何數(shù)據(jù)的讀寫都需要借助緩沖區(qū),你可以把緩沖區(qū)理解成一個(gè)數(shù)組,當(dāng)要寫入數(shù)據(jù)時(shí)就向數(shù)組中存值,當(dāng)要讀取數(shù)據(jù)時(shí)就從數(shù)組中取值。

          創(chuàng)建緩沖區(qū)

          對(duì)于緩沖區(qū)的創(chuàng)建,JDK提供了兩種方式(以字節(jié)緩沖區(qū)ByteBuffer為例):

          1. allocate
          2. wrap

          其中allocate用于創(chuàng)建一個(gè)指定大小的緩沖區(qū):

          @Test
          public void buffer(){
              // 指定長(zhǎng)度的緩沖區(qū)
              ByteBuffer byteBuffer = ByteBuffer.allocate(5);
              for (int i = 0; i < 5; i++) {
                  // 從緩沖區(qū)中獲取數(shù)據(jù)
                  System.out.print(byteBuffer.get() + "\t");
              }
          }

          運(yùn)行結(jié)果:

          0 0 0 0 0

          通過(guò)緩沖區(qū)的get方法可以獲取緩沖區(qū)中的數(shù)據(jù),初始為數(shù)據(jù)類型的零值。

          而wrap方法可以創(chuàng)建一個(gè)指定內(nèi)容的緩沖區(qū):

          @Test
          public void buffer(){
              // 指定內(nèi)容的緩沖區(qū)
              ByteBuffer wrap = ByteBuffer.wrap("test".getBytes());
              for (int i = 0; i < 4; i++) {
                  System.out.print((char) wrap.get() + "\t");
              }
          }

          運(yùn)行結(jié)果:

          t e s t 

          那么緩沖區(qū)內(nèi)部的具體結(jié)構(gòu)是如何的呢?數(shù)據(jù)的存取是怎樣進(jìn)行的呢?你需要了解緩沖區(qū)中的幾個(gè)標(biāo)記:

          1. position:當(dāng)前索引位置
          2. limit:最大索引位置
          3. capacity:緩沖區(qū)的總?cè)萘?/section>
          4. remaining:緩沖區(qū)的剩余容量

          來(lái)創(chuàng)建一個(gè)容量為10的緩沖區(qū),然后分別輸出這些標(biāo)記的值:

          @Test
          public void buffer(){
              ByteBuffer allocate = ByteBuffer.allocate(10);
              System.out.print(allocate.position() + "\t"); // 當(dāng)前索引位置
              System.out.print(allocate.limit() + "\t"); // 最大索引位置,初始等于緩沖區(qū)大小
              System.out.print(allocate.capacity() + "\t"); // 返回緩沖區(qū)的總長(zhǎng)度
              System.out.print(allocate.remaining() + "\t"); // 剩余能操作的容量(limit - position)
          }

          運(yùn)行結(jié)果:

          0 10 10 10

          每個(gè)標(biāo)記的位置如下圖所示:

          img

          position指向的是當(dāng)前索引位置,當(dāng)向緩沖區(qū)中添加數(shù)據(jù)時(shí),position便會(huì)隨之移動(dòng),而limit指向的是最大索引位置(初始等于capacity),即position最大不會(huì)等于limit,remaining為緩沖區(qū)的剩余容量,remaining = limit - position

          向緩沖區(qū)添加數(shù)據(jù)

          現(xiàn)在向緩沖區(qū)添加一個(gè)數(shù)據(jù):

          // 向緩沖區(qū)添加一個(gè)字節(jié)
          allocate.put((byte97);

          此時(shí)緩沖區(qū)標(biāo)記會(huì)如何變化呢?首先position會(huì)右移一位,然后remaining變?yōu)?,其它的不影響,如下圖所示:

          img

          我們可以試驗(yàn)一下是不是這樣:

          @Test
          public void buffer(){
              ByteBuffer allocate = ByteBuffer.allocate(10);
              // 向緩沖區(qū)添加一個(gè)字節(jié)
              allocate.put((byte97);
              System.out.print(allocate.position() + "\t");
              System.out.print(allocate.limit() + "\t");
              System.out.print(allocate.capacity() + "\t");
              System.out.print(allocate.remaining() + "\t");
          }

          運(yùn)行結(jié)果:

          1 10 10 9

          結(jié)果正如我們所料。

          緩沖區(qū)的put方法還能夠傳遞一個(gè)數(shù)組,將一串?dāng)?shù)據(jù)進(jìn)行添加:

          // 向緩沖區(qū)添加一個(gè)字節(jié)
          allocate.put("0123456789".getBytes());

          若是當(dāng)前緩沖區(qū)已經(jīng)滿了,則再向一個(gè)滿的緩沖區(qū)添加數(shù)據(jù)會(huì)拋出異常:

          @Test
          public void buffer(){
              ByteBuffer allocate = ByteBuffer.allocate(10);
              allocate.put("0123456789".getBytes());
              allocate.put((byte1);
          }

          運(yùn)行結(jié)果:

          java.nio.BufferOverflowException
           at java.nio.Buffer.nextPutIndex(Buffer.java:521)
              at java.nio.HeapByteBuffer.put(HeapByteBuffer.java:169)
              at com.wwj.nio.BufferDemo.buffer(BufferDemo.java:144)

          通過(guò)緩沖區(qū)的hasRemaining方法可以判斷當(dāng)前緩沖區(qū)是否還能夠繼續(xù)添加數(shù)據(jù):

          @Test
          public void buffer(){
              ByteBuffer allocate = ByteBuffer.allocate(10);
              System.out.println(allocate.hasRemaining());
              allocate.put("0123456789".getBytes());
              System.out.println(allocate.hasRemaining());
          }

          運(yùn)行結(jié)果:

          true
          false

          緩沖區(qū)支持動(dòng)態(tài)修改標(biāo)記位置,以達(dá)到重新寫入的需求:

          @Test
          public void buffer(){
              ByteBuffer allocate = ByteBuffer.allocate(10);
              allocate.put("0123456789".getBytes());
              // 修改當(dāng)前索引位置
              allocate.position(0);
              allocate.put((byte1);
              System.out.print(allocate.position() + "\t");
              System.out.print(allocate.limit() + "\t");
              System.out.print(allocate.capacity() + "\t");
              System.out.print(allocate.remaining() + "\t");
          }

          運(yùn)行結(jié)果:

          1 10 10 9

          把position位置修改為0之后,又相當(dāng)于對(duì)一個(gè)空的緩沖區(qū)進(jìn)行操作了。

          讀取緩沖區(qū)數(shù)據(jù)

          接下來(lái)介紹一下緩沖區(qū)數(shù)據(jù)的讀取,在最開(kāi)始我們已經(jīng)使用過(guò)get方法來(lái)讀取緩沖區(qū)的數(shù)據(jù)了,如下:

          @Test
          public void buffer() {
              ByteBuffer allocate = ByteBuffer.allocate(10);
              allocate.put("0123".getBytes());
              for (int i = 0; i < 4; i++) {
                  System.out.print(allocate.get() + "\t");
              }
          }

          大家可以猜一猜運(yùn)行結(jié)果是什么呢:

          0 0 0 0

          也許有同學(xué)很奇怪,為什么添加的數(shù)據(jù)沒(méi)有被讀取出來(lái),其實(shí),如果你掌握了緩沖區(qū)中的標(biāo)記,就能明白是為什么。

          在創(chuàng)建了一個(gè)容量為10的緩沖區(qū)之后,標(biāo)記如下圖所示:

          img

          當(dāng)向緩沖區(qū)添加了一個(gè)字節(jié)數(shù)組后,標(biāo)記發(fā)生了變化:

          img

          此時(shí)我們調(diào)用get方法進(jìn)行讀取,它將從position位置也就是索引4位置開(kāi)始往后讀取,這樣讀取到的數(shù)據(jù)當(dāng)然就是0了,若是想讀取添加到緩沖區(qū)中的數(shù)據(jù),則需要將position移動(dòng)到索引0位置才行,不過(guò)JDK已經(jīng)提供了這樣的方法給我們:

          @Test
          public void buffer() {
              ByteBuffer allocate = ByteBuffer.allocate(10);
              allocate.put("0123".getBytes());
              // 切換為讀模式
              allocate.flip();
              for (int i = 0; i < allocate.limit(); i++) {
                  System.out.print((char) allocate.get() + "\t");
              }
          }

          運(yùn)行結(jié)果:

          0 1 2 3

          查看一下flip方法的源碼:

          public final Buffer flip() {
              limit = position;
              position = 0;
              mark = -1;
              return this;
          }

          關(guān)鍵就在于limit = positionposition = 0,通過(guò)改變這兩個(gè)標(biāo)記后:

          img

          position重新回到了索引0的位置,這樣就可以進(jìn)行正常的讀取了,而limit也修改為了寫入數(shù)據(jù)的末尾位置,可以通過(guò)判斷l(xiāng)imit來(lái)終止讀取條件。

          與寫入數(shù)據(jù)一樣,緩沖區(qū)在讀取數(shù)據(jù)的時(shí)候,也會(huì)不停地移動(dòng)position,當(dāng)所有數(shù)據(jù)都被讀取后,再次讀取數(shù)據(jù)將會(huì)拋出異常,因?yàn)閜osition必須小于等于limit:

          @Test
          public void buffer() {
              ByteBuffer allocate = ByteBuffer.allocate(10);
              allocate.put("0123".getBytes());
              // 切換為讀模式
              allocate.flip();
              for (int i = 0; i < allocate.limit(); i++) {
                  System.out.print((char) allocate.get() + "\t");
              }
              allocate.get();
          }

          運(yùn)行結(jié)果:

          0 1 2 3 
          java.nio.BufferUnderflowException
           at java.nio.Buffer.nextGetIndex(Buffer.java:500)
           at java.nio.HeapByteBuffer.get(HeapByteBuffer.java:135)
           at com.wwj.nio.BufferDemo.buffer(BufferDemo.java:148)

          但是通過(guò)索引讀取數(shù)據(jù)將不會(huì)判斷position是否小于等于limit,也不會(huì)移動(dòng)position:

          @Test
          public void buffer() {
              ByteBuffer allocate = ByteBuffer.allocate(10);
              allocate.put("0123".getBytes());
              // 切換為讀模式
              allocate.flip();
              for (int i = 0; i < allocate.limit(); i++) {
                  System.out.print((char) allocate.get() + "\t");
              }
              // 通過(guò)索引讀取數(shù)據(jù)
              System.out.println((char) allocate.get(1));
          }

          運(yùn)行結(jié)果:

          0 1 2 3 1

          數(shù)據(jù)讀取完畢后,若是想重新對(duì)該緩沖區(qū)進(jìn)行讀取,則可以將position手動(dòng)置為0,也可以調(diào)用JDK提供的方法:

          // 調(diào)用rewind方法可以將當(dāng)前索引重置為0
          allocate.rewind();

          rewind方法內(nèi)部也是對(duì)position進(jìn)行賦值為0的操作:

          public final Buffer rewind() {
              position = 0;
              mark = -1;
              return this;
          }

          若是想重新對(duì)緩沖區(qū)進(jìn)行寫入,則調(diào)用clear方法:

          // 切換寫模式,此時(shí)會(huì)將當(dāng)前索引置為0,將最大索引置為緩沖區(qū)容量
          allocate.clear();

          注意rewind方法和clear方法的區(qū)別,它們雖然都會(huì)將position置為0,但是clear方法還會(huì)將limit置為capacity的值,所以當(dāng)想要再次讀取緩沖區(qū)中的數(shù)據(jù)時(shí),則可以調(diào)用rewind方法;當(dāng)想要再次寫入數(shù)據(jù)到緩沖區(qū)時(shí),則可以調(diào)用clear方法。

          來(lái)驗(yàn)證一下:

          @Test
          public void buffer() {
              ByteBuffer allocate = ByteBuffer.allocate(10);
              allocate.put("0123".getBytes());
              // 切換為讀模式
              allocate.flip();
              for(int i = 0;i <allocate.limit();++i){
                  allocate.get();
              }
              System.out.print("position:" + allocate.position() + "\t");
              System.out.print("limit:" + allocate.limit() + "\t");
              System.out.print("capacity:" + allocate.capacity() + "\t");
              System.out.print("remaining:" + allocate.remaining() + "\t");
              System.out.println();

              allocate.rewind();
              System.out.print("position:" + allocate.position() + "\t");
              System.out.print("limit:" + allocate.limit() + "\t");
              System.out.print("capacity:" + allocate.capacity() + "\t");
              System.out.print("remaining:" + allocate.remaining() + "\t");
              System.out.println();

              allocate.clear();
              System.out.print("position:" + allocate.position() + "\t");
              System.out.print("limit:" + allocate.limit() + "\t");
              System.out.print("capacity:" + allocate.capacity() + "\t");
              System.out.print("remaining:" + allocate.remaining() + "\t");
          }

          運(yùn)行結(jié)果:

          position:4  limit:4  capacity:10  remaining:0 
          position:0  limit:4  capacity:10  remaining:4 
          position:0  limit:10 capacity:10  remaining:10 

              

          1、2點(diǎn)睡10點(diǎn)起不算熬夜?

          2、被捧上天的Scrum敏捷管理為何不受大廠歡迎了?

          3、離大譜!win10/11又爆多個(gè)離奇Bug,速看避坑!

          4、你為什么不交女朋友,是因?yàn)椴幌雴???/a>

          5、微軟欲閉源VS Code的C#擴(kuò)展惹眾怒

          6、上能寫代碼,下要“揍”黑客,還有什么不是程序員的“鍋”?

          點(diǎn)

          點(diǎn)

          點(diǎn)點(diǎn)

          點(diǎn)在看

          瀏覽 95
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲美女网站 | 亚洲无码免费视频一区二区三区四虎 | 国产性爱一区二区 | 天堂在线视频精品 | 国产精品无码性爱视频 |