欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java IO流深入理解

 更新時間:2021年07月29日 10:56:41   作者:evanYang_  
這篇文章主要介紹了java IO流的深入理解,下面和小編來一起學習一下吧,希望能給你帶來幫助,也希望您能夠多多關注腳本之家的更多內(nèi)容

阻塞(Block)和非阻塞(Non-Block)

阻塞和非阻塞是進程在訪問數(shù)據(jù)的時候,數(shù)據(jù)是否準備就緒的一種處理方式,當數(shù)據(jù)沒有準備的時候。

**阻塞:**往往需要等待緩沖區(qū)中的數(shù)據(jù)準備好過后才處理其他的事情,否者一直等待在那里

**非阻塞:**當我們進程訪問我們的數(shù)據(jù)緩沖區(qū)的時候,如果數(shù)據(jù)沒有準備好則直接返回,不會等待。如果數(shù)據(jù)已經(jīng)準備好,也直接返回。

同步(Synchronization)和異步(Asynchronous)

同步和異步都是基于應用程序和操作系統(tǒng)處理IO事件所采用的方式。比如同步:是應用程序要直接參與IO讀寫的操作。異步:所有的IO讀寫交給操作系統(tǒng)去處理,應用程序只需要等待通知。

同步方式在處理IO事件的時候,必須阻塞在某個方法上面等待我們的IO事件完成(阻塞IO事件或者通過輪詢IO事件的方式),對于異步來說,所有的IO讀寫都交給了操作系統(tǒng)。這個時候,我們可以去做其他的事情,并不需要去完成真正的IO操作,當操作完成iO后,會給我們的應用程序一個通知。

**同步:**阻塞到IO事件,阻塞到read或者write。這個時候我們就完全不能做自己的事情。讓讀寫方法加入到線程里面,然后阻塞線程來實現(xiàn),對線程的性能開銷比較大。

BIO與NIO對比

IO模型 BIO NIO
通信 面向流(鄉(xiāng)村公路) 面向緩沖(高速公路,多路復用技術)
處理 阻塞IO(多線程) 非阻塞IO(反應堆Reactor)
觸發(fā) 選擇器 輪詢機制

面向流與面向緩沖

java NIO和BIO之間第一個最大的區(qū)別是,BIO是面向流的,NIO是面向緩沖的。Java BIO面向流意味著每次從流中讀一個或多個字節(jié),直至讀取所有的字節(jié),它們沒有被緩存在任何地方。此外,它不能前后移動流中的數(shù)據(jù)。如果需要前后移動從流中讀取的數(shù)據(jù),需要先將它緩存到一個緩沖區(qū)。Java NIO的緩沖導向方法略有不同。數(shù)據(jù)讀取到一個它稍后處理的緩沖區(qū),需要時可在緩沖區(qū)前后移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否緩沖區(qū)包含了所有您需要處理的數(shù)據(jù)。而且,需確保當更多的數(shù)據(jù)讀入緩沖區(qū)時,不要覆蓋緩沖區(qū)里尚未處理的數(shù)據(jù)。

阻塞與非阻塞

Java BIO的各種流是阻塞的。這意味著,當一個線程調用read()和write()時,該線程被阻塞,直到有一些數(shù)據(jù)被讀取,或數(shù)據(jù)完全寫入。該線程在此期間不能再干任何事情了。java NIO的非阻塞模式,使一個線程從某通道發(fā)送請求讀取數(shù)據(jù),但是它僅能得到目前可用的數(shù)據(jù),如果目前沒有數(shù)據(jù)可用時,就什么多不會獲取。而不是保持線程阻塞,所以直至數(shù)據(jù)變得可以讀取之前,該線程可以繼續(xù)做其他的事情。

非阻塞寫也是如此。一個線程請求寫入一些數(shù)據(jù)到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。線程通常將非阻塞IO的空閑時間用于在其他通道上執(zhí)行IO操作,所以一個單獨的線程現(xiàn)在可以管理多個輸入和輸出通道。

選擇器的問世

java NIO的選擇器(Selector)允許一個單獨的線程來監(jiān)視多個輸入通道,你可以注冊多個通道使用一個選擇器,然后使用一個單獨的線程來“選擇”通道,這些通道里已經(jīng)有可以處理的輸入,或者選擇已準備寫入的通道。這種選擇機制,使得一個單獨的線程很容易來管理多個通道。

Java NIO三件套

在NIO中有幾個核心對象需要掌握:緩沖器(Buffer)、選擇器(Selector)、通道(Channel)

緩沖區(qū)Buffer

緩沖區(qū)實際上是一個容器對象,更直接的說,其實就是一個數(shù)組,在NIO庫中,所有數(shù)據(jù)都是用緩沖區(qū)出來的。在讀取數(shù)據(jù)時,它是直接讀到緩沖區(qū)的;在寫入數(shù)據(jù)時,它也是寫入到緩沖區(qū)的;任何時候訪問NIO中的數(shù)據(jù),都是將它放到緩沖區(qū)中。而在面向流I/O系統(tǒng)中,所有數(shù)據(jù)都是直接寫入或者直接將數(shù)據(jù)讀取到Stream對象中。

在NIO中,所有的緩沖區(qū)類型都繼承于抽象類Buffer,最常用的就是ByteBuffer,對于java中的基本類型,基本都有一個具體Buffer類型與之相對于,他們之間的extend關系如下圖所示。

在這里插入圖片描述

eg:

  public static void main(String[] args) {
        //new NIOServerDemo(8080).listen();

        // 分配新的 int 緩沖區(qū),參數(shù)為緩沖區(qū)容量
        // 新緩沖區(qū)的當前位置將為零,其界限(限制位置)將為其容量。它將具有一個底層實現(xiàn)數(shù)組,其數(shù)組偏移量將為零。
        IntBuffer buffer = IntBuffer.allocate(8);
        for (int i = 0; i < buffer.capacity(); ++i) {
            int j = 2 * (i + 1);
            // 將給定整數(shù)寫入此緩沖區(qū)的當前位置,當前位置遞增
            buffer.put(j);
        }
        // 重設此緩沖區(qū),將限制設置為當前位置,然后將當前位置設置為 0
        buffer.flip();
        // 查看在當前位置和限制位置之間是否有元素
        while (buffer.hasRemaining()) {
            // 讀取此緩沖區(qū)當前位置的整數(shù),然后當前位置遞增
            int j = buffer.get();
            System.out.print(j + " ");
        }
    }
2 4 6 8 10 12 14 16 
Process finished with exit code 0

Buffer的基本的原理

在談到緩沖區(qū)時,我們說緩沖區(qū)對象本質上是一個數(shù)組,但它其實是一個特殊的數(shù)組,緩沖區(qū)對象內(nèi)置了一些機制,能夠跟蹤和記錄緩沖區(qū)的狀態(tài)變化情況。如果我們使用get()方法從緩沖區(qū)獲取數(shù)據(jù)或者使用put()方法把數(shù)據(jù)寫入緩沖區(qū),都會引起緩沖區(qū)狀態(tài)的變化。

在緩沖區(qū)中,最重要的屬性有下面三個,它們一起合作完成對緩沖區(qū)內(nèi)部的狀態(tài)的變化跟蹤。

position: 指定下一個將要被寫入或者讀取的元素索引,它的值由get()/put()方法自動更新,在新創(chuàng)建一個Buffer對象時,position被初始化為0.

limit:指定還有多少數(shù)據(jù)需要取出(在從緩沖區(qū)寫入通道時),或者還有多少空間可以放入數(shù)據(jù)(在從通道讀入緩沖區(qū)時)

**capacity:**制定了可以存儲在緩沖區(qū)中的最大數(shù)據(jù)容量,實際上,它制定了底層數(shù)組的大小,或者至少是指定了準許我們使用的底層數(shù)組的容量。

以上三個屬性值之間有一些相對大小的關系: 0<=positon<=limit<=capacity。如果我們創(chuàng)建一個新的容量大小為10的ByteBuffer對象,在初始化的時候,positon設置為0,limit和capacity被設置為10,在以后使用ByteBuffer對象過程中,capacity的值不會再發(fā)生變化,而其他兩個將會隨著使用而變化。

eg:

package com.evan.netty.nio.demo;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @author evanYang
 * @version 1.0
 * @date 2021/7/26 11:29
 */
public class BufferDemo {
    public static void main(String[] args) throws IOException {

        FileInputStream fin = new FileInputStream("D://evan.txt");
        FileChannel fc = fin.getChannel();
        //先分配一個10大小的緩沖區(qū)
        ByteBuffer buffer = ByteBuffer.allocate(10);
        outPut("初始化",buffer);

        fc.read(buffer);
        outPut("調用read()方法",buffer);

        buffer.flip();
        outPut("調用flip()",buffer);

        //判斷有沒有可讀數(shù)據(jù)
        while (buffer.remaining()>0){
            byte b = buffer.get();
        }

        outPut("調用get()",buffer);

        //可以理解為解鎖
        buffer.clear();
        outPut("調用clear()",buffer);

        fin.close();
    }

    /**
     * 打印緩存實時狀況
     * @param step
     * @param buffer
     */
    public static void outPut(String step, Buffer buffer){
        System.out.println(step+":");
        System.out.println("capacity: "+buffer.capacity()+",");
        System.out.println("position: "+buffer.position()+",");
        System.out.println("limit: "+buffer.limit());
        System.out.println();
    }
}

文件中的數(shù)據(jù)

在這里插入圖片描述

輸出結果:

在這里插入圖片描述

運行結果我們已經(jīng)可以知道,四個屬性值分別如圖所示:

在這里插入圖片描述

我們可以從管道中讀取一些數(shù)據(jù)到緩沖區(qū),注意從通道讀取數(shù)據(jù),相當于往緩沖區(qū)寫入數(shù)據(jù)。如果讀取4個自己的數(shù)據(jù),則此時position的值為4,即下一個將要被下入的字節(jié)索引是4,而limit仍然是10,如下圖所示:

在這里插入圖片描述

下一步把讀取的數(shù)據(jù)寫入到輸出管道中,相當于從緩沖區(qū)中讀取數(shù)據(jù),在此之前,必須調用flip()方法,該方法將會完成兩件事:

1,把limit設置為當前的positon值

2,把position設置為0

由于position被設置為0,所以可以保證在下一步輸出時讀取到的是緩沖區(qū)中的第一個字節(jié),而limit被設置為當前的position,可以保證讀取的數(shù)據(jù)正好是之前寫入到緩沖區(qū)的數(shù)據(jù),如下圖所示。

在這里插入圖片描述

現(xiàn)在調用get()方法從緩沖區(qū)讀取數(shù)據(jù)寫入到輸出通道,這會導致position的增加而limit保持不變,單position不會超過limit的值,所以在讀取我們之前寫入到緩沖區(qū)中的4個自己之后,position和limit的值都為4.如下圖所示。

在這里插入圖片描述

在從緩沖區(qū)讀取數(shù)據(jù)完畢后,limit的值仍然保持在我們調用flip()方法時的值,調用clean()方法能夠把所有的狀態(tài)設置為初始值。

緩沖區(qū)分配

在前面的幾個例子中,我們已經(jīng)看過了,在創(chuàng)建一個緩沖對象時,會調用靜態(tài)方法allocate()來指定緩沖區(qū)的容量,其實調用allocate()相當于創(chuàng)建一個指定大小的數(shù)組,并把它包裝為緩沖區(qū)對象?;蛘呶覀円部梢灾苯訉⒁粋€現(xiàn)有的數(shù)組,包裝為緩沖區(qū)對對象

選擇器Selector

傳統(tǒng)的Server/Client 模式會基于TPR(Thread per Request),服務器會為每個客戶端請求建立一個線程,由該線程單獨負責處理一個客戶請求。這種模式帶來的一個問題就是線程數(shù)量的劇增,大量的線程會增大服務器的開銷。大多數(shù)的實現(xiàn)為了避免這個問題,都采用了線程池模型,并設置線程池模型的最大數(shù)量,這又帶來了新的問題,如果線程池中有200個線程,而有200個用戶都在進行大文件下載,會導致第201個用戶的請求無法及時處理,即便第201個用戶只想請求一個幾kb大小的頁面。傳統(tǒng)的Server/Client模式如下圖所示。

在這里插入圖片描述

NIO 中非阻塞I/O采用了基于Reactor模式的工作模式,I/O調用不會被阻塞,相反是注冊感興趣的特定I/O事件,如可讀數(shù)據(jù)到達,新的套接字連接等等,在發(fā)生特定事件時,系統(tǒng)在通知我們。NIO中實現(xiàn)非阻塞I/O的核心對象就是Selector,Selector就是注冊各種I/O事件地方,而且當那些事件發(fā)生時,就是這個對象告訴我們所發(fā)生的事件。如下圖所示。

在這里插入圖片描述

從圖中可以看出,當有讀或寫等任何注冊的時間發(fā)生時,可以從Selector中獲得相應的SelectionKey,同時從SelectionKey中可以找到發(fā)生的事件和該事件所發(fā)生的具體SelectableChannel,以獲得客戶端發(fā)送過來的數(shù)據(jù)。

使用NIO中非阻塞I/O編寫服務器處理程序,大體上可以分為下面三個步驟:

1,向Selector對象注冊感興趣的事件。

2,從Selector中獲取感興趣的事件。

3,根據(jù)不同的事件進行相應的處理。

通道Channel

通道是一個對象,通過它可以讀取和寫入數(shù)據(jù),當然了所有數(shù)據(jù)都通過Buffer對象來處理。我們永遠不會將字節(jié)直接寫入通道中,相反是將數(shù)據(jù)寫入包含一個或者多個字節(jié)的緩存區(qū)。同樣不會直接從通道中讀取字節(jié),而是將數(shù)據(jù)從通道讀入緩存區(qū),再從緩沖區(qū)獲取這個字節(jié),而是將數(shù)據(jù)從通道讀入緩沖區(qū),再從緩沖區(qū)獲取這個字節(jié)。

在NIO中,提供了多種通道對象,而所有的通道對象都實現(xiàn)了Channel接口。它們之間的繼承關系如下圖所示:

在這里插入圖片描述

使用NIO讀取數(shù)據(jù)

在前面我們說過,任何時候讀取數(shù)據(jù),都不是直接從通道讀取,而是從通道讀取到緩沖區(qū)。所以使用NIO讀取數(shù)據(jù)可以分為下面三個步驟:

1,從FileInputStream獲取Channel

2,創(chuàng)建Buffer

3,將數(shù)據(jù)從Channel讀取到Buffer中

package com.evan.netty.nio.demo;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @author evanYang
 * @version 1.0
 * @date 2021/7/26 16:15
 */
public class FileInputDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fin=new FileInputStream("D://evan.txt");
        FileChannel channel = fin.getChannel();

        ByteBuffer allocate = ByteBuffer.allocate(1024);
        //讀取數(shù)據(jù)到緩沖區(qū)
        channel.read(allocate);

        allocate.flip();
        while (allocate.remaining()>0){
            byte b = allocate.get();
            System.out.println(b);
        }
        fin.close();
    }
}

使用NIO寫入數(shù)據(jù)

使用NIO寫入數(shù)據(jù)與讀取數(shù)據(jù)的過程類似,同樣數(shù)據(jù)不是直接寫入通道,而是寫入緩沖區(qū),可以分為下面三個步驟:

1,從FileputStream獲取channel。

2,創(chuàng)建Buffer

3,將數(shù)據(jù)從Channel寫入到Buffer中,

package com.evan.netty.nio.demo;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @author evanYang
 * @version 1.0
 * @date 2021/7/26 16:33
 */
public class FileOutPutDemo {
    static private final byte message[] ={83, 111, 109, 101, 32, 98, 121, 116, 101, 115, 46 };
    public static void main(String[] args) throws IOException {
        FileOutputStream fout=new FileOutputStream("D://evan.txt");

        FileChannel channel = fout.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        for (int i = 0; i < message.length; i++) {
            buffer.put(message[i]);
        }
        buffer.flip();
        channel.write(buffer);
        fout.close();
    }
}

在這里插入圖片描述

IO多路復用

我們試想一下這樣的現(xiàn)實場景。

100桌客人到店點菜

方法A:

服務員都把僅有的一份菜單遞給其中一桌客人,然后站在這個客人身旁等待客人完成點菜過程。。。。。

方法B:

老板馬上新雇傭99名服務員,同時印制99本新的菜單。沒人服務一桌客人。

在這里插入圖片描述

方法C:

改進點菜的方式,當客人到店后,自己申請一本菜單。想好自己要點的菜,然后呼叫服務員。服務員站在自己身邊記錄客人點的菜的內(nèi)容。

在這里插入圖片描述

  • 到店情況 :并發(fā)量
    • 到店情況不理想時,一個服務員一本菜單,就足夠了
  • 客人:服務端請求
  • 點餐內(nèi)容:客服端發(fā)送的實際數(shù)據(jù)
  • 老板:操作系統(tǒng)
  • 人力成本:系統(tǒng)資源
  • 菜單:文件狀態(tài)描述符(FD)。操作系統(tǒng)對于一個進程能夠同時持有的文件狀態(tài)描述符的個數(shù)是有限制的,在linux系統(tǒng)中,$Ulimit -n 查看這個限制值,當然也是可以(并且應該)進行內(nèi)核參數(shù)調整的
  • 服務員:操作系統(tǒng)內(nèi)核用于IO操作的線程(內(nèi)核線程)
  • 廚師:應用程序線程(當然廚房就是應用程序進程)
    • 方法A:同步IO
    • 方法B:同步IO
    • 方法C:多路復用IO

總結

本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關注腳本之家的更多內(nèi)容!

相關文章

  • springboot?vue項目管理前后端實現(xiàn)編輯功能

    springboot?vue項目管理前后端實現(xiàn)編輯功能

    這篇文章主要為大家介紹了springboot?vue項目管理前后端實現(xiàn)編輯功能,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05
  • Java Runtime用法實戰(zhàn)案例

    Java Runtime用法實戰(zhàn)案例

    這篇文章主要介紹了Java Runtime用法,結合實例形式分析了Runtime針對系統(tǒng)內(nèi)存、處理器信息的獲取相關操作技巧,需要的朋友可以參考下
    2019-07-07
  • Java讀取文件的簡單實現(xiàn)方法

    Java讀取文件的簡單實現(xiàn)方法

    這篇文章主要介紹了Java讀取文件的簡單實現(xiàn)方法,通過一個讀取txt格式的log文件為例,詳細的講述了Java讀取文件的方法及原理,需要的朋友可以參考下
    2014-09-09
  • Java實現(xiàn)汽車租賃系統(tǒng)

    Java實現(xiàn)汽車租賃系統(tǒng)

    這篇文章介紹了Java實現(xiàn)汽車租賃系統(tǒng)的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • 詳解Spring Cloud Stream使用延遲消息實現(xiàn)定時任務(RabbitMQ)

    詳解Spring Cloud Stream使用延遲消息實現(xiàn)定時任務(RabbitMQ)

    這篇文章主要介紹了詳解Spring Cloud Stream使用延遲消息實現(xiàn)定時任務(RabbitMQ),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-01-01
  • Java中對null進行強制類型轉換的方法

    Java中對null進行強制類型轉換的方法

    小編對null進行強轉會不會拋錯,非常的好奇,下面小編通過實例代碼給大家介紹Java中對null進行強制類型轉換的方法,感興趣的朋友參考下吧
    2018-09-09
  • java實現(xiàn)簡單美女拼圖游戲

    java實現(xiàn)簡單美女拼圖游戲

    這篇文章主要介紹了java實現(xiàn)簡單美女拼圖游戲的相關資料,需要的朋友可以參考下
    2015-03-03
  • Java面試題沖刺第二十七天--JVM2

    Java面試題沖刺第二十七天--JVM2

    這篇文章主要為大家分享了最有價值的三道關于JVM的面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結構和算法相關的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下
    2021-08-08
  • java地理坐標系及投影間轉換代碼示例

    java地理坐標系及投影間轉換代碼示例

    在地圖投影中,經(jīng)常需要將坐標從不同的坐標系之間進行轉換,下面這篇文章主要給大家介紹了關于java地理坐標系及投影間轉換的相關資料,需要的朋友可以參考下
    2024-08-08
  • spring?cloud?gateway中配置uri三種方式

    spring?cloud?gateway中配置uri三種方式

    gateway?組件是SpringCloud?組件中的網(wǎng)關組件,主要是解決路由轉發(fā)的問題,跟nginx有點類似,區(qū)別是nginx多用在前端上,gateway用在后端上,本文給大家介紹的非常詳細,需要的朋友參考下吧
    2023-08-08

最新評論