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

Android進階之從IO到NIO的模型機制演進

 更新時間:2023年01月30日 16:51:05   作者:layz4android  
這篇文章主要為大家介紹了Android進階之從IO到NIO的模型機制演進詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

其實IO操作相較于服務端,客戶端做的并不多,基本的場景就是讀寫文件的時候會使用到InputStream或者OutputStream,然而客戶端能做的就是發(fā)起一個讀寫的指令,真正的操作是內(nèi)核層通過ioctl指令執(zhí)行讀寫操作,因為每次的IO操作都涉及到了線程的操作,因此會有性能上的損耗,那么從本篇文章開始,我們將進入IO的世界,了解IO到NIO機制的演進,從底層關(guān)注序列化的原理。

1 Basic IO模型

那么在Java(Kotlin)中,IO主要分為兩種:Basic IO 和 Net IO;Basic IO是我們在開發(fā)當中常用的一些IO流,例如:

FileInputStream://文件輸入流
FileOutputStream://文件輸出流
BufferedInputStream://緩存字節(jié)輸入流
BufferedOutputStream://緩存字節(jié)輸入流,此類數(shù)據(jù)流為了提高讀寫效率,可以緩存數(shù)據(jù)到buffer,通過flush一起寫入;內(nèi)核分配內(nèi)存為一頁4K,但是Java緩沖區(qū)默認是8K
ObjectInputStream
ObjectOutputStream:// 將數(shù)據(jù)序列化處理
RandomAccessFile://提供位移數(shù)據(jù)插入

對于前面的幾個數(shù)據(jù)流,我就不介紹用法了,對于最后一個RandomAccessFile,我想簡單介紹一下,因為很多伙伴們可能不知道RandomAccessFile的存在,這里曾經(jīng)有個面試題:

假設有一個5G的文件,我想在文章的末尾追加一段話,我該怎么處理?或者我指定任意位置添加一部分文字內(nèi)容,該怎么處理?

很多伙伴看到這個問題之后,一拍腦門說:先通過FileInputStream把文件讀寫進來,然后再在末尾追加一部分內(nèi)容組合成新的字節(jié)流,然后再通過FileOutputStream寫入到新的文件中。

完蛋,直接pass掉!因為前提這里已經(jīng)是5G的文件了,如果通過FileInputStream讀寫,大概率就會直接OOM! 所以如果知道RandomAccessFile的存在,這些就不是問題了。

fun testAccessFile() {
    //file文件
    val file = File("/storage/emulated/0/NewTextFile.txt")
    val accessFile = RandomAccessFile(file, "rw")
    //先寫一段
    val text = "IO主要分為兩種:Basic IO 和 Net IO;"
    accessFile.write(text.toByteArray())
    //再等5s
    Thread.sleep(5000)
    accessFile.seek(5)
    accessFile.write("seek to pos 5".toByteArray())
    accessFile.close()
}

首先我們常見一個RandomAccessFile,傳入要讀寫的文件,首先寫入一段話,然后等到5s后,調(diào)用RandomAccessFile的seek方法,此時指針就是移動到了文件第五個字符的位置,然后又寫入了一些文字。

所以按照這種思想,回到前面的問題,即便是5G的文件,也不需要進行讀寫操作獲取之前的全部數(shù)據(jù)就能夠?qū)崿F(xiàn)零內(nèi)存追加;當然還有一個場景也會經(jīng)常用到,就是斷點續(xù)傳。

1.1 RandomAccessFile的緩沖區(qū)和BufferedInputStream緩沖區(qū)的區(qū)別

首先我先簡單介紹下BufferedInputStream的緩存區(qū)效果,系統(tǒng)內(nèi)核緩存區(qū)默認為4K,當緩存區(qū)滿4K之后會進行磁盤的寫入;那么在Java中是對其做了優(yōu)化處理,將緩存區(qū)變?yōu)?K,當緩存區(qū)超過8K之后,會將數(shù)據(jù)復制給到內(nèi)核緩存。

fun testBuffer() {
        val file = File("/storage/emulated/0/NewTextFile.txt")
        val bis = BufferedOutputStream(FileOutputStream(file))
        val text = "8888888888888888".toByteArray()
        bis.write(text, 0, text.size)
//        bis.flush()
    }

例如上面的案例,此時App的內(nèi)存緩存區(qū)沒有滿,那么如果不調(diào)用flush,那么數(shù)據(jù)不會寫到磁盤文件中,只有當緩沖區(qū)滿了之后,才會復制到內(nèi)核空間緩存區(qū)。

fun testAccessFile() {
    //file文件
    val file = File("/storage/emulated/0/NewTextFile.txt")
    val accessFile = RandomAccessFile(file, "rw")
    //先寫一段
    val text = "IO主要分為兩種:Basic IO 和 Net IO;"
    accessFile.write(text.toByteArray())
    //再等5s
    Thread.sleep(5000)
    accessFile.seek(5)
    val channel = accessFile.channel
    val mapper = channel.map(FileChannel.MapMode.READ_WRITE, channel.position(), channel.size())
    mapper.put("seek to pos 5".toByteArray())
}

如果按照BufferedOutputStream的思想,我們往緩沖區(qū)寫數(shù)據(jù),沒有flush就不會有復制的操作,那么我們實際看到的是數(shù)據(jù)還是寫進去了。

其實MappedByteBuffer,是提供了一個類似于mmap性質(zhì)的能力,實現(xiàn)了App緩沖區(qū)與內(nèi)核緩沖區(qū)的橋接或者映射。

當App寫入緩存數(shù)據(jù)的時候,直接映射到了內(nèi)核緩存區(qū),完成了磁盤的讀寫操作。

1.2 Basic IO模型底層原理

其實對于基礎(chǔ)的IO模型,也就是Basic IO的實現(xiàn)是阻塞的,其實我們也可以自己驗證,在主線程中進行讀寫操作就是阻塞的。

那么對于IO來說,主要分為兩個階段:

(1)數(shù)據(jù)準備階段;這里是由Java實現(xiàn)的,寫入到JVM中;

(2)復制階段;內(nèi)核空間復制用戶空間緩存數(shù)據(jù),這部分需要調(diào)用內(nèi)核函數(shù)(ioctl、sync),完成復制的工作。

剩下的磁盤寫入操作就完全是由內(nèi)核完成的,如果對于讀寫操作有疑問的,可以去看看下面這篇對于Binder底層原理的介紹。

Android Framework原理 -- Binder驅(qū)動源碼分析

對于傳統(tǒng)的Socket來說,這種屬于Net IO,本質(zhì)也是阻塞性質(zhì)的,例如App進程想要獲取一些數(shù)據(jù),

上圖展示了read操作的整個調(diào)度過程:

(1)當App調(diào)用系統(tǒng)方法想要獲取某些數(shù)據(jù)的時候,首先系統(tǒng)內(nèi)核會等待數(shù)據(jù)從網(wǎng)絡中到達,這個過程內(nèi)核處于阻塞的狀態(tài)

(2)等到數(shù)據(jù)到達之后,就會將網(wǎng)絡數(shù)據(jù)復制到用戶空間的緩沖區(qū)中,并通知App進程復制數(shù)據(jù)成功,此時App中其他業(yè)務才能夠繼續(xù)執(zhí)行。

所以整個過程中,App處于阻塞狀態(tài),而在高并發(fā)的場景中(客戶端很少,這里拿服務端來舉例),例如10000QPS(每秒10000次查詢操作),此時如果采用IO阻塞模型,帶來的后果就是CPU極速拉滿最終可能導致熔斷,所以針對這種情況,出現(xiàn)了NIO模型。

2 NIO模型

相對于IO模型來說,NIO模型做的優(yōu)化是通過輪詢機制獲取內(nèi)核的數(shù)據(jù)等待狀態(tài),看下圖:

當一次詢問發(fā)出之后,如果當前內(nèi)核還是數(shù)據(jù)等待狀態(tài),那么內(nèi)核空間會被”掛起“,此時App進程可以做其他的事情,等到下一次輪詢時間到了之后,再次發(fā)起詢問,如果此時已經(jīng)拿到了數(shù)據(jù),那么就會進行復制操作,將數(shù)據(jù)放入用戶進程緩沖區(qū)。

那么對此,java.nio包下提供了很多非阻塞IO的API,例如我們前面提到的MappedByteBuffer。其實還是前面我們探討的一個問題,在Android的場景下,很難碰到高并發(fā)的場景,所以基本上也很難用到這個,但是對于NIO模型的原理我們需要掌握透徹,在面試中可能會涉及到這些問題。

3 OKIO

最后介紹一個IO模型---OKIO,如果使用到OkHttp的伙伴們應該已經(jīng)見到過這個,但是沒有實際地去研究,為啥要引入這個okio三方庫。

首先okio是OkHttp團隊基于Basic IO研發(fā)的一套自己的IO體系,為啥要搞一個這個玩意出來呢?通過前面我們分析Basic IO存在的一些問題,首先 Basic IO是阻塞的,而且在客戶端端如果頻繁地進行網(wǎng)絡請求,而且網(wǎng)絡請求是雙向的,從客戶端發(fā)出請求,服務端返回響應,那么這個過程必定會使用到InputStream和OutputStream。

因為OkHttp是有自己的緩存策略的,如果使用到緩存,那么對于InputStream就需要一個buffer,對于OutputStream也需要一個buffer,每次讀寫操作都需要兩個buffer來做支撐,因此針對這種場景,okio在底層做了處理。

具體的處理就是不再使用byte[]數(shù)組存儲數(shù)據(jù),而是采用Segment數(shù)據(jù)結(jié)構(gòu)。有熟悉Segment的伙伴應該知道,它是一個數(shù)組的雙向鏈表,其中data就是一個byte數(shù)組,其中有next和pre兩個指針。

internal class Segment {
  @JvmField val data: ByteArray
  /** The next byte of application data byte to read in this segment.  */
  @JvmField var pos: Int = 0
  /** The first byte of available data ready to be written to.  */
  @JvmField var limit: Int = 0
  /** True if other segments or byte strings use the same byte array.  */
  @JvmField var shared: Boolean = false
  /** True if this segment owns the byte array and can append to it, extending `limit`.  */
  @JvmField var owner: Boolean = false
  /** Next segment in a linked or circularly-linked list.  */
  @JvmField var next: Segment? = null
  /** Previous segment in a circularly-linked list.  */
  @JvmField var prev: Segment? = null

當進行讀寫操作的時候,都會往Segment中寫入,就是將InputStream和OutputStream需要創(chuàng)建的緩沖區(qū)合并。

這里需要說明一點,okio屬于OkHttp內(nèi)部核心IO框架,并不是單獨拿出來任意業(yè)務方可以使用,所以對于okio的具體實現(xiàn)原理,后續(xù)會放在OkHttp框架原理中做詳細的介紹。

以上就是Android進階之從IO到NIO的模型機制演進的詳細內(nèi)容,更多關(guān)于Android模型從IO到NIO機制的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android UI效果之繪圖篇(一)

    Android UI效果之繪圖篇(一)

    這篇文章主要介紹了Android UI效果之繪圖篇,針對Android開發(fā)中的UI效果設計模塊進行講解,感興趣的小伙伴們可以參考一下
    2016-02-02
  • Android即時通訊設計(騰訊IM接入和WebSocket接入)

    Android即時通訊設計(騰訊IM接入和WebSocket接入)

    本文主要介紹了Android即時通訊設計(騰訊IM接入和WebSocket接入),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-04-04
  • android 仿微信聊天氣泡效果實現(xiàn)思路

    android 仿微信聊天氣泡效果實現(xiàn)思路

    微信聊天窗口的信息效果類似iphone上的短信效果,以氣泡的形式展現(xiàn),實現(xiàn)這種效果主要用到ListView和BaseAdapter,配合布局以及相關(guān)素材,接下來為大家介紹下如何實現(xiàn)
    2013-04-04
  • Android使用Room數(shù)據(jù)庫解決本地持久化的操作

    Android使用Room數(shù)據(jù)庫解決本地持久化的操作

    Room 是一個持久性庫,屬于 Android Jetpack 的一部分,Room 是 SQLite 數(shù)據(jù)庫之上的一個抽象層,Room 并不直接使用 SQLite,而是負責簡化數(shù)據(jù)庫設置和配置以及與數(shù)據(jù)庫交互方面的瑣碎工作,本文介紹了Android使用Room數(shù)據(jù)庫解決本地持久化的操作,需要的朋友可以參考下
    2024-09-09
  • Android集成zxing掃碼框架功能

    Android集成zxing掃碼框架功能

    這篇文章主要介紹了Android集成zxing掃碼框架功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-04-04
  • Android中判斷網(wǎng)絡是否連接實例詳解

    Android中判斷網(wǎng)絡是否連接實例詳解

    這篇文章主要介紹了Android中判斷網(wǎng)絡是否連接實例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-01-01
  • Android錄音并且輸出為Mp4文件的方法教程

    Android錄音并且輸出為Mp4文件的方法教程

    這篇文章主要給大家介紹了關(guān)于Android錄音并且輸出為Mp4文件的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-08-08
  • Android 線程之自定義帶消息循環(huán)Looper的實例

    Android 線程之自定義帶消息循環(huán)Looper的實例

    這篇文章主要介紹了Android 線程之自定義帶消息循環(huán)Looper的實例的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-10-10
  • Android仿微信之界面導航篇(1)

    Android仿微信之界面導航篇(1)

    這篇文章主要為大家詳細介紹了Android仿微信之界面導航篇,教大家實現(xiàn)滑動界面效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • 在android中如何用Java加載解析so

    在android中如何用Java加載解析so

    我們在android開發(fā)項目過程中都必然會更so加載打交道,那么so加載在系統(tǒng)中的順序和流程是怎樣的,我們就有必要對這個加載過程進行熟悉了解掌握
    2021-10-10

最新評論