溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

深入淺析Java NIO中的IO模型

發(fā)布時(shí)間:2020-11-20 15:38:06 來(lái)源:億速云 閱讀:138 作者:Leah 欄目:編程語(yǔ)言

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)深入淺析Java NIO中的IO模型,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

一.什么是同步?什么是異步

  同步和異步的概念出來(lái)已經(jīng)很久了,網(wǎng)上有關(guān)同步和異步的說(shuō)法也有很多。以下是我個(gè)人的理解:

  同步就是:如果有多個(gè)任務(wù)或者事件要發(fā)生,這些任務(wù)或者事件必須逐個(gè)地進(jìn)行,一個(gè)事件或者任務(wù)的執(zhí)行會(huì)導(dǎo)致整個(gè)流程的暫時(shí)等待,這些事件沒(méi)有辦法并發(fā)地執(zhí)行;

  異步就是:如果有多個(gè)任務(wù)或者事件發(fā)生,這些事件可以并發(fā)地執(zhí)行,一個(gè)事件或者任務(wù)的執(zhí)行不會(huì)導(dǎo)致整個(gè)流程的暫時(shí)等待。

  這就是同步和異步。舉個(gè)簡(jiǎn)單的例子,假如有一個(gè)任務(wù)包括兩個(gè)子任務(wù)A和B,對(duì)于同步來(lái)說(shuō),當(dāng)A在執(zhí)行的過(guò)程中,B只有等待,直至A執(zhí)行完畢,B才能執(zhí)行;而對(duì)于異步就是A和B可以并發(fā)地執(zhí)行,B不必等待A執(zhí)行完畢之后再執(zhí)行,這樣就不會(huì)由于A的執(zhí)行導(dǎo)致整個(gè)任務(wù)的暫時(shí)等待。

  如果還不理解,可以先看下面這2段代碼:

void fun1() {
 }
 void fun2() {
 }
 void function(){
   fun1();
   fun2()
   .....
   .....
 }

   這段代碼就是典型的同步,在方法function中,fun1在執(zhí)行的過(guò)程中會(huì)導(dǎo)致后續(xù)的fun2無(wú)法執(zhí)行,fun2必須等待fun1執(zhí)行完畢才可以執(zhí)行。

  接著看下面這段代碼:

void fun1() {
  }
void fun2() {
}
void function(){
  new Thread(){
    public void run() {
      fun1();
    }
  }.start();
  new Thread(){
    public void run() {
      fun2();
    }
  }.start();
  .....
  .....
}

   這段代碼是一種典型的異步,fun1的執(zhí)行不會(huì)影響到fun2的執(zhí)行,并且fun1和fun2的執(zhí)行不會(huì)導(dǎo)致其后續(xù)的執(zhí)行過(guò)程處于暫時(shí)的等待。

  事實(shí)上,同步和異步是一個(gè)非常廣的概念,它們的重點(diǎn)在于多個(gè)任務(wù)和事件發(fā)生時(shí),一個(gè)事件的發(fā)生或執(zhí)行是否會(huì)導(dǎo)致整個(gè)流程的暫時(shí)等待。我覺(jué)得可以將同步和異步與Java中的synchronized關(guān)鍵字聯(lián)系起來(lái)進(jìn)行類(lèi)比。當(dāng)多個(gè)線程同時(shí)訪問(wèn)一個(gè)變量時(shí),每個(gè)線程訪問(wèn)該變量就是一個(gè)事件,對(duì)于同步來(lái)說(shuō),就是這些線程必須逐個(gè)地來(lái)訪問(wèn)該變量,一個(gè)線程在訪問(wèn)該變量的過(guò)程中,其他線程必須等待;而對(duì)于異步來(lái)說(shuō),就是多個(gè)線程不必逐個(gè)地訪問(wèn)該變量,可以同時(shí)進(jìn)行訪問(wèn)。
  因此,個(gè)人覺(jué)得同步和異步可以表現(xiàn)在很多方面,但是記住其關(guān)鍵在于多個(gè)任務(wù)和事件發(fā)生時(shí),一個(gè)事件的發(fā)生或執(zhí)行是否會(huì)導(dǎo)致整個(gè)流程的暫時(shí)等待。一般來(lái)說(shuō),可以通過(guò)多線程的方式來(lái)實(shí)現(xiàn)異步,但是千萬(wàn)記住不要將多線程和異步畫(huà)上等號(hào),異步只是宏觀上的一個(gè)模式,采用多線程來(lái)實(shí)現(xiàn)異步只是一種手段,并且通過(guò)多進(jìn)程的方式也可以實(shí)現(xiàn)異步。

二.什么是阻塞?什么是非阻塞?

  在前面介紹了同步和異步的區(qū)別,這一節(jié)來(lái)看一下阻塞和非阻塞的區(qū)別。

  阻塞就是:當(dāng)某個(gè)事件或者任務(wù)在執(zhí)行過(guò)程中,它發(fā)出一個(gè)請(qǐng)求操作,但是由于該請(qǐng)求操作需要的條件不滿足,那么就會(huì)一直在那等待,直至條件滿足;

  非阻塞就是:當(dāng)某個(gè)事件或者任務(wù)在執(zhí)行過(guò)程中,它發(fā)出一個(gè)請(qǐng)求操作,如果該請(qǐng)求操作需要的條件不滿足,會(huì)立即返回一個(gè)標(biāo)志信息告知條件不滿足,不會(huì)一直在那等待。

  這就是阻塞和非阻塞的區(qū)別。也就是說(shuō)阻塞和非阻塞的區(qū)別關(guān)鍵在于當(dāng)發(fā)出請(qǐng)求一個(gè)操作時(shí),如果條件不滿足,是會(huì)一直等待還是返回一個(gè)標(biāo)志信息。

  舉個(gè)簡(jiǎn)單的例子:

  假如我要讀取一個(gè)文件中的內(nèi)容,如果此時(shí)文件中沒(méi)有內(nèi)容可讀,對(duì)于同步來(lái)說(shuō)就是會(huì)一直在那等待,直至文件中有內(nèi)容可讀;而對(duì)于非阻塞來(lái)說(shuō),就會(huì)直接返回一個(gè)標(biāo)志信息告知文件中暫時(shí)無(wú)內(nèi)容可讀。

  在網(wǎng)上有一些朋友將同步和異步分別與阻塞和非阻塞畫(huà)上等號(hào),事實(shí)上,它們是兩組完全不同的概念。注意,理解這兩組概念的區(qū)別對(duì)于后面IO模型的理解非常重要。

  同步和異步著重點(diǎn)在于多個(gè)任務(wù)的執(zhí)行過(guò)程中,一個(gè)任務(wù)的執(zhí)行是否會(huì)導(dǎo)致整個(gè)流程的暫時(shí)等待;

  而阻塞和非阻塞著重點(diǎn)在于發(fā)出一個(gè)請(qǐng)求操作時(shí),如果進(jìn)行操作的條件不滿足是否會(huì)返會(huì)一個(gè)標(biāo)志信息告知條件不滿足。

  理解阻塞和非阻塞可以同線程阻塞類(lèi)比地理解,當(dāng)一個(gè)線程進(jìn)行一個(gè)請(qǐng)求操作時(shí),如果條件不滿足,則會(huì)被阻塞,即在那等待條件滿足。

三.什么是阻塞IO?什么是非阻塞IO?

  在了解阻塞IO和非阻塞IO之前,先看下一個(gè)具體的IO操作過(guò)程是怎么進(jìn)行的。

  通常來(lái)說(shuō),IO操作包括:對(duì)硬盤(pán)的讀寫(xiě)、對(duì)socket的讀寫(xiě)以及外設(shè)的讀寫(xiě)。

  當(dāng)用戶線程發(fā)起一個(gè)IO請(qǐng)求操作(本文以讀請(qǐng)求操作為例),內(nèi)核會(huì)去查看要讀取的數(shù)據(jù)是否就緒,對(duì)于阻塞IO來(lái)說(shuō),如果數(shù)據(jù)沒(méi)有就緒,則會(huì)一直在那等待,直到數(shù)據(jù)就緒;對(duì)于非阻塞IO來(lái)說(shuō),如果數(shù)據(jù)沒(méi)有就緒,則會(huì)返回一個(gè)標(biāo)志信息告知用戶線程當(dāng)前要讀的數(shù)據(jù)沒(méi)有就緒。當(dāng)數(shù)據(jù)就緒之后,便將數(shù)據(jù)拷貝到用戶線程,這樣才完成了一個(gè)完整的IO讀請(qǐng)求操作,也就是說(shuō)一個(gè)完整的IO讀請(qǐng)求操作包括兩個(gè)階段:

  1)查看數(shù)據(jù)是否就緒;

  2)進(jìn)行數(shù)據(jù)拷貝(內(nèi)核將數(shù)據(jù)拷貝到用戶線程)。

  那么阻塞(blocking IO)和非阻塞(non-blocking IO)的區(qū)別就在于第一個(gè)階段,如果數(shù)據(jù)沒(méi)有就緒,在查看數(shù)據(jù)是否就緒的過(guò)程中是一直等待,還是直接返回一個(gè)標(biāo)志信息。

  Java中傳統(tǒng)的IO都是阻塞IO,比如通過(guò)socket來(lái)讀數(shù)據(jù),調(diào)用read()方法之后,如果數(shù)據(jù)沒(méi)有就緒,當(dāng)前線程就會(huì)一直阻塞在read方法調(diào)用那里,直到有數(shù)據(jù)才返回;而如果是非阻塞IO的話,當(dāng)數(shù)據(jù)沒(méi)有就緒,read()方法應(yīng)該返回一個(gè)標(biāo)志信息,告知當(dāng)前線程數(shù)據(jù)沒(méi)有就緒,而不是一直在那里等待。

四.什么是同步IO?什么是異步IO?

  我們先來(lái)看一下同步IO和異步IO的定義,在《Unix網(wǎng)絡(luò)編程》一書(shū)中對(duì)同步IO和異步IO的定義是這樣的:

  A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes.
  An asynchronous I/O operation does not cause the requesting process to be blocked.

  從字面的意思可以看出:同步IO即 如果一個(gè)線程請(qǐng)求進(jìn)行IO操作,在IO操作完成之前,該線程會(huì)被阻塞;

  而異步IO為 如果一個(gè)線程請(qǐng)求進(jìn)行IO操作,IO操作不會(huì)導(dǎo)致請(qǐng)求線程被阻塞。

  事實(shí)上,同步IO和異步IO模型是針對(duì)用戶線程和內(nèi)核的交互來(lái)說(shuō)的:

  對(duì)于同步IO:當(dāng)用戶發(fā)出IO請(qǐng)求操作之后,如果數(shù)據(jù)沒(méi)有就緒,需要通過(guò)用戶線程或者內(nèi)核不斷地去輪詢數(shù)據(jù)是否就緒,當(dāng)數(shù)據(jù)就緒時(shí),再將數(shù)據(jù)從內(nèi)核拷貝到用戶線程;

  而異步IO:只有IO請(qǐng)求操作的發(fā)出是由用戶線程來(lái)進(jìn)行的,IO操作的兩個(gè)階段都是由內(nèi)核自動(dòng)完成,然后發(fā)送通知告知用戶線程IO操作已經(jīng)完成。也就是說(shuō)在異步IO中,不會(huì)對(duì)用戶線程產(chǎn)生任何阻塞。

  這是同步IO和異步IO關(guān)鍵區(qū)別所在,同步IO和異步IO的關(guān)鍵區(qū)別反映在數(shù)據(jù)拷貝階段是由用戶線程完成還是內(nèi)核完成。所以說(shuō)異步IO必須要有操作系統(tǒng)的底層支持。

  注意同步IO和異步IO與阻塞IO和非阻塞IO是不同的兩組概念。

  阻塞IO和非阻塞IO是反映在當(dāng)用戶請(qǐng)求IO操作時(shí),如果數(shù)據(jù)沒(méi)有就緒,是用戶線程一直等待數(shù)據(jù)就緒,還是會(huì)收到一個(gè)標(biāo)志信息這一點(diǎn)上面的。也就是說(shuō),阻塞IO和非阻塞IO是反映在IO操作的第一個(gè)階段,在查看數(shù)據(jù)是否就緒時(shí)是如何處理的。

五.五種IO模型

  在《Unix網(wǎng)絡(luò)編程》一書(shū)中提到了五種IO模型,分別是:阻塞IO、非阻塞IO、多路復(fù)用IO、信號(hào)驅(qū)動(dòng)IO以及異步IO。

  下面就分別來(lái)介紹一下這5種IO模型的異同。

1.阻塞IO模型

  最傳統(tǒng)的一種IO模型,即在讀寫(xiě)數(shù)據(jù)過(guò)程中會(huì)發(fā)生阻塞現(xiàn)象。

  當(dāng)用戶線程發(fā)出IO請(qǐng)求之后,內(nèi)核會(huì)去查看數(shù)據(jù)是否就緒,如果沒(méi)有就緒就會(huì)等待數(shù)據(jù)就緒,而用戶線程就會(huì)處于阻塞狀態(tài),用戶線程交出CPU。當(dāng)數(shù)據(jù)就緒之后,內(nèi)核會(huì)將數(shù)據(jù)拷貝到用戶線程,并返回結(jié)果給用戶線程,用戶線程才解除block狀態(tài)。

  典型的阻塞IO模型的例子為:

 data = socket.read();

   如果數(shù)據(jù)沒(méi)有就緒,就會(huì)一直阻塞在read方法。

2.非阻塞IO模型

  當(dāng)用戶線程發(fā)起一個(gè)read操作后,并不需要等待,而是馬上就得到了一個(gè)結(jié)果。如果結(jié)果是一個(gè)error時(shí),它就知道數(shù)據(jù)還沒(méi)有準(zhǔn)備好,于是它可以再次發(fā)送read操作。一旦內(nèi)核中的數(shù)據(jù)準(zhǔn)備好了,并且又再次收到了用戶線程的請(qǐng)求,那么它馬上就將數(shù)據(jù)拷貝到了用戶線程,然后返回。

  所以事實(shí)上,在非阻塞IO模型中,用戶線程需要不斷地詢問(wèn)內(nèi)核數(shù)據(jù)是否就緒,也就說(shuō)非阻塞IO不會(huì)交出CPU,而會(huì)一直占用CPU。

  典型的非阻塞IO模型一般如下:

while(true){
  data = socket.read();
  if(data!= error){
    處理數(shù)據(jù)
    break;
  }
}

   但是對(duì)于非阻塞IO就有一個(gè)非常嚴(yán)重的問(wèn)題,在while循環(huán)中需要不斷地去詢問(wèn)內(nèi)核數(shù)據(jù)是否就緒,這樣會(huì)導(dǎo)致CPU占用率非常高,因此一般情況下很少使用while循環(huán)這種方式來(lái)讀取數(shù)據(jù)。

3.多路復(fù)用IO模型

  多路復(fù)用IO模型是目前使用得比較多的模型。Java NIO實(shí)際上就是多路復(fù)用IO。

  在多路復(fù)用IO模型中,會(huì)有一個(gè)線程不斷去輪詢多個(gè)socket的狀態(tài),只有當(dāng)socket真正有讀寫(xiě)事件時(shí),才真正調(diào)用實(shí)際的IO讀寫(xiě)操作。因?yàn)樵诙嗦窂?fù)用IO模型中,只需要使用一個(gè)線程就可以管理多個(gè)socket,系統(tǒng)不需要建立新的進(jìn)程或者線程,也不必維護(hù)這些線程和進(jìn)程,并且只有在真正有socket讀寫(xiě)事件進(jìn)行時(shí),才會(huì)使用IO資源,所以它大大減少了資源占用。

  在Java NIO中,是通過(guò)selector.select()去查詢每個(gè)通道是否有到達(dá)事件,如果沒(méi)有事件,則一直阻塞在那里,因此這種方式會(huì)導(dǎo)致用戶線程的阻塞。

  也許有朋友會(huì)說(shuō),我可以采用 多線程+ 阻塞IO 達(dá)到類(lèi)似的效果,但是由于在多線程 + 阻塞IO 中,每個(gè)socket對(duì)應(yīng)一個(gè)線程,這樣會(huì)造成很大的資源占用,并且尤其是對(duì)于長(zhǎng)連接來(lái)說(shuō),線程的資源一直不會(huì)釋放,如果后面陸續(xù)有很多連接的話,就會(huì)造成性能上的瓶頸。

  而多路復(fù)用IO模式,通過(guò)一個(gè)線程就可以管理多個(gè)socket,只有當(dāng)socket真正有讀寫(xiě)事件發(fā)生才會(huì)占用資源來(lái)進(jìn)行實(shí)際的讀寫(xiě)操作。因此,多路復(fù)用IO比較適合連接數(shù)比較多的情況。

  另外多路復(fù)用IO為何比非阻塞IO模型的效率高是因?yàn)樵诜亲枞鸌O中,不斷地詢問(wèn)socket狀態(tài)時(shí)通過(guò)用戶線程去進(jìn)行的,而在多路復(fù)用IO中,輪詢每個(gè)socket狀態(tài)是內(nèi)核在進(jìn)行的,這個(gè)效率要比用戶線程要高的多。

  不過(guò)要注意的是,多路復(fù)用IO模型是通過(guò)輪詢的方式來(lái)檢測(cè)是否有事件到達(dá),并且對(duì)到達(dá)的事件逐一進(jìn)行響應(yīng)。因此對(duì)于多路復(fù)用IO模型來(lái)說(shuō),一旦事件響應(yīng)體很大,那么就會(huì)導(dǎo)致后續(xù)的事件遲遲得不到處理,并且會(huì)影響新的事件輪詢。

4.信號(hào)驅(qū)動(dòng)IO模型

  在信號(hào)驅(qū)動(dòng)IO模型中,當(dāng)用戶線程發(fā)起一個(gè)IO請(qǐng)求操作,會(huì)給對(duì)應(yīng)的socket注冊(cè)一個(gè)信號(hào)函數(shù),然后用戶線程會(huì)繼續(xù)執(zhí)行,當(dāng)內(nèi)核數(shù)據(jù)就緒時(shí)會(huì)發(fā)送一個(gè)信號(hào)給用戶線程,用戶線程接收到信號(hào)之后,便在信號(hào)函數(shù)中調(diào)用IO讀寫(xiě)操作來(lái)進(jìn)行實(shí)際的IO請(qǐng)求操作。

5.異步IO模型

  異步IO模型才是最理想的IO模型,在異步IO模型中,當(dāng)用戶線程發(fā)起read操作之后,立刻就可以開(kāi)始去做其它的事。而另一方面,從內(nèi)核的角度,當(dāng)它受到一個(gè)asynchronous read之后,它會(huì)立刻返回,說(shuō)明read請(qǐng)求已經(jīng)成功發(fā)起了,因此不會(huì)對(duì)用戶線程產(chǎn)生任何block。然后,內(nèi)核會(huì)等待數(shù)據(jù)準(zhǔn)備完成,然后將數(shù)據(jù)拷貝到用戶線程,當(dāng)這一切都完成之后,內(nèi)核會(huì)給用戶線程發(fā)送一個(gè)信號(hào),告訴它read操作完成了。也就說(shuō)用戶線程完全不需要實(shí)際的整個(gè)IO操作是如何進(jìn)行的,只需要先發(fā)起一個(gè)請(qǐng)求,當(dāng)接收內(nèi)核返回的成功信號(hào)時(shí)表示IO操作已經(jīng)完成,可以直接去使用數(shù)據(jù)了。

  也就說(shuō)在異步IO模型中,IO操作的兩個(gè)階段都不會(huì)阻塞用戶線程,這兩個(gè)階段都是由內(nèi)核自動(dòng)完成,然后發(fā)送一個(gè)信號(hào)告知用戶線程操作已完成。用戶線程中不需要再次調(diào)用IO函數(shù)進(jìn)行具體的讀寫(xiě)。這點(diǎn)是和信號(hào)驅(qū)動(dòng)模型有所不同的,在信號(hào)驅(qū)動(dòng)模型中,當(dāng)用戶線程接收到信號(hào)表示數(shù)據(jù)已經(jīng)就緒,然后需要用戶線程調(diào)用IO函數(shù)進(jìn)行實(shí)際的讀寫(xiě)操作;而在異步IO模型中,收到信號(hào)表示IO操作已經(jīng)完成,不需要再在用戶線程中調(diào)用iO函數(shù)進(jìn)行實(shí)際的讀寫(xiě)操作。

  注意,異步IO是需要操作系統(tǒng)的底層支持,在Java 7中,提供了Asynchronous IO。

  前面四種IO模型實(shí)際上都屬于同步IO,只有最后一種是真正的異步IO,因?yàn)闊o(wú)論是多路復(fù)用IO還是信號(hào)驅(qū)動(dòng)模型,IO操作的第2個(gè)階段都會(huì)引起用戶線程阻塞,也就是內(nèi)核進(jìn)行數(shù)據(jù)拷貝的過(guò)程都會(huì)讓用戶線程阻塞。

六.兩種高性能IO設(shè)計(jì)模

  在傳統(tǒng)的網(wǎng)絡(luò)服務(wù)設(shè)計(jì)模式中,有兩種比較經(jīng)典的模式:

  一種是 多線程,一種是線程池。

  對(duì)于多線程模式,也就說(shuō)來(lái)了client,服務(wù)器就會(huì)新建一個(gè)線程來(lái)處理該client的讀寫(xiě)事件,如下圖所示:

深入淺析Java NIO中的IO模型

  這種模式雖然處理起來(lái)簡(jiǎn)單方便,但是由于服務(wù)器為每個(gè)client的連接都采用一個(gè)線程去處理,使得資源占用非常大。因此,當(dāng)連接數(shù)量達(dá)到上限時(shí),再有用戶請(qǐng)求連接,直接會(huì)導(dǎo)致資源瓶頸,嚴(yán)重的可能會(huì)直接導(dǎo)致服務(wù)器崩潰。

  因此,為了解決這種一個(gè)線程對(duì)應(yīng)一個(gè)客戶端模式帶來(lái)的問(wèn)題,提出了采用線程池的方式,也就說(shuō)創(chuàng)建一個(gè)固定大小的線程池,來(lái)一個(gè)客戶端,就從線程池取一個(gè)空閑線程來(lái)處理,當(dāng)客戶端處理完讀寫(xiě)操作之后,就交出對(duì)線程的占用。因此這樣就避免為每一個(gè)客戶端都要?jiǎng)?chuàng)建線程帶來(lái)的資源浪費(fèi),使得線程可以重用。

  但是線程池也有它的弊端,如果連接大多是長(zhǎng)連接,因此可能會(huì)導(dǎo)致在一段時(shí)間內(nèi),線程池中的線程都被占用,那么當(dāng)再有用戶請(qǐng)求連接時(shí),由于沒(méi)有可用的空閑線程來(lái)處理,就會(huì)導(dǎo)致客戶端連接失敗,從而影響用戶體驗(yàn)。因此,線程池比較適合大量的短連接應(yīng)用。

  因此便出現(xiàn)了下面的兩種高性能IO設(shè)計(jì)模式:Reactor和Proactor。

  在Reactor模式中,會(huì)先對(duì)每個(gè)client注冊(cè)感興趣的事件,然后有一個(gè)線程專門(mén)去輪詢每個(gè)client是否有事件發(fā)生,當(dāng)有事件發(fā)生時(shí),便順序處理每個(gè)事件,當(dāng)所有事件處理完之后,便再轉(zhuǎn)去繼續(xù)輪詢,如下圖所示: 

深入淺析Java NIO中的IO模型

  從這里可以看出,上面的五種IO模型中的多路復(fù)用IO就是采用Reactor模式。注意,上面的圖中展示的 是順序處理每個(gè)事件,當(dāng)然為了提高事件處理速度,可以通過(guò)多線程或者線程池的方式來(lái)處理事件。

  在Proactor模式中,當(dāng)檢測(cè)到有事件發(fā)生時(shí),會(huì)新起一個(gè)異步操作,然后交由內(nèi)核線程去處理,當(dāng)內(nèi)核線程完成IO操作之后,發(fā)送一個(gè)通知告知操作已完成,可以得知,異步IO模型采用的就是Proactor模式。

上述就是小編為大家分享的深入淺析Java NIO中的IO模型了,如果剛好有類(lèi)似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI