本篇內(nèi)容主要講解“Java IO模型與Java網(wǎng)絡(luò)編程模型的對(duì)比”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Java IO模型與Java網(wǎng)絡(luò)編程模型的對(duì)比”吧!
哈密ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)建站的ssl證書銷售渠道,可以享受市場價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:028-86922220(備注:SSL證書合作)期待與您的合作!作者:cooffeelis
鏈接:
https://www.jianshu.com/p/511b9cffbdac
來源:簡書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。
常用的5種IO模型:
blocking IO
nonblocking IO
IO multiplexing
signal driven IO
asynchronous IO
再說一下IO發(fā)生時(shí)涉及的對(duì)象和步驟:
對(duì)于一個(gè)network IO (這里我們以read舉例),它會(huì)涉及到兩個(gè)系統(tǒng)對(duì)象:
一個(gè)是調(diào)用這個(gè)IO的process (or thread)
一個(gè)就是系統(tǒng)內(nèi)核(kernel)
當(dāng)一個(gè)read操作發(fā)生時(shí),它會(huì)經(jīng)歷兩個(gè)階段:
等待數(shù)據(jù)準(zhǔn)備,比如accept(), recv()等待數(shù)據(jù)
(Waiting for the data to be ready)
將數(shù)據(jù)從內(nèi)核拷貝到進(jìn)程中, 比如 accept()接受到請(qǐng)求,recv()接收連接發(fā)送的數(shù)據(jù)后需要復(fù)制到內(nèi)核,再從內(nèi)核復(fù)制到進(jìn)程用戶空間(Copying the data from the kernel to the process)
對(duì)于socket流而言,數(shù)據(jù)的流向經(jīng)歷兩個(gè)階段:
第一步通常涉及等待網(wǎng)絡(luò)上的數(shù)據(jù)分組到達(dá),然后被復(fù)制到內(nèi)核的某個(gè)緩沖區(qū)。
第二步把數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到應(yīng)用進(jìn)程緩沖區(qū)。
記住這兩點(diǎn)很重要,因?yàn)檫@些IO Model的區(qū)別就是在兩個(gè)階段上各有不同的情況。
在linux中,默認(rèn)情況下所有的socket都是blocking,一個(gè)典型的讀操作流程大概是這樣:
阻塞IO流程
當(dāng)用戶進(jìn)程調(diào)用了recvfrom這個(gè)系統(tǒng)調(diào)用,kernel就開始了IO的第一個(gè)階段:準(zhǔn)備數(shù)據(jù)(對(duì)于網(wǎng)絡(luò)IO來說,很多時(shí)候數(shù)據(jù)在一開始還沒有到達(dá)。比如,還沒有收到一個(gè)完整的UDP包。這個(gè)時(shí)候kernel就要等待足夠的數(shù)據(jù)到來)。這個(gè)過程需要等待,也就是說數(shù)據(jù)被拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)中是需要一個(gè)過程的。而在用戶進(jìn)程這邊,整個(gè)進(jìn)程會(huì)被阻塞(當(dāng)然,是進(jìn)程自己選擇的阻塞)。當(dāng)kernel一直等到數(shù)據(jù)準(zhǔn)備好了,它就會(huì)將數(shù)據(jù)從kernel中拷貝到用戶內(nèi)存,然后kernel返回結(jié)果,用戶進(jìn)程才解除block的狀態(tài),重新運(yùn)行起來。
所以,blocking IO的特點(diǎn)就是在IO執(zhí)行的兩個(gè)階段都被block了。
linux下,可以通過設(shè)置socket使其變?yōu)閚on-blocking。當(dāng)對(duì)一個(gè)non-blocking socket執(zhí)行讀操作時(shí),流程是這個(gè)樣子:
非阻塞 I/O 流程
當(dāng)用戶進(jìn)程發(fā)出read操作時(shí),如果kernel中的數(shù)據(jù)還沒有準(zhǔn)備好,那么它并不會(huì)block用戶進(jìn)程,而是立刻返回一個(gè)error。從用戶進(jìn)程角度講 ,它發(fā)起一個(gè)read操作后,并不需要等待,而是馬上就得到了一個(gè)結(jié)果。用戶進(jìn)程判斷結(jié)果是一個(gè)error時(shí),它就知道數(shù)據(jù)還沒有準(zhǔn)備好,于是它可以再次發(fā)送read操作。一旦kernel中的數(shù)據(jù)準(zhǔn)備好了,并且又再次收到了用戶進(jìn)程的system call,那么它馬上就將數(shù)據(jù)拷貝到了用戶內(nèi)存,然后返回。
所以,nonblocking IO的特點(diǎn)是用戶進(jìn)程需要不斷的主動(dòng)詢問kernel數(shù)據(jù)好了沒有。
值得注意的是,此時(shí)的非阻塞IO只是應(yīng)用到等待數(shù)據(jù)上,當(dāng)真正有數(shù)據(jù)到達(dá)執(zhí)行recvfrom的時(shí)候,還是同步阻塞IO來的, 從圖中的copy data from kernel to user可以看出
IO multiplexing就是我們說的select,poll,epoll,有些地方也稱這種IO方式為event driven IO。select/epoll的好處就在于單個(gè)process就可以同時(shí)處理多個(gè)網(wǎng)絡(luò)連接的IO。它的基本原理就是select,poll,epoll這個(gè)function會(huì)不斷的輪詢所負(fù)責(zé)的所有socket,當(dāng)某個(gè)socket有數(shù)據(jù)到達(dá)了,就通知用戶進(jìn)程。
I/O 多路復(fù)用流程
這個(gè)圖和blocking IO的圖其實(shí)并沒有太大的不同,事實(shí)上,還更差一些。因?yàn)檫@里需要使用兩個(gè)system call (select 和 recvfrom),而blocking IO只調(diào)用了一個(gè)system call (recvfrom)。但是,用select的優(yōu)勢在于它可以同時(shí)處理多個(gè)connection。
所以,如果處理的連接數(shù)不是很高的話,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延遲還更大。select/epoll的優(yōu)勢并不是對(duì)于單個(gè)連接能處理得更快,而是在于能處理更多的連接。)
IO復(fù)用的實(shí)現(xiàn)方式目前主要有select、poll和epoll。
select和poll的原理基本相同:
注冊(cè)待偵聽的fd(這里的fd創(chuàng)建時(shí)最好使用非阻塞)
每次調(diào)用都去檢查這些fd的狀態(tài),當(dāng)有一個(gè)或者多個(gè)fd就緒的時(shí)候返回
返回結(jié)果中包括已就緒和未就緒的fd
相比select,poll解決了單個(gè)進(jìn)程能夠打開的文件描述符數(shù)量有限制這個(gè)問題:select受限于FD_SIZE的限制,如果修改則需要修改這個(gè)宏重新編譯內(nèi)核;而poll通過一個(gè)pollfd數(shù)組向內(nèi)核傳遞需要關(guān)注的事件,避開了文件描述符數(shù)量限制。
此外,select和poll共同具有的一個(gè)很大的缺點(diǎn)就是包含大量fd的數(shù)組被整體復(fù)制于用戶態(tài)和內(nèi)核態(tài)地址空間之間,開銷會(huì)隨著fd數(shù)量增多而線性增大。
select和poll就類似于上面說的就餐方式。但當(dāng)你每次都去詢問時(shí),老板會(huì)把所有你點(diǎn)的飯菜都輪詢一遍再告訴你情況,當(dāng)大量飯菜很長時(shí)間都不能準(zhǔn)備好的情況下是很低效的。于是,老板有些不耐煩了,就讓廚師每做好一個(gè)菜就通知他。這樣每次你再去問的時(shí)候,他會(huì)直接把已經(jīng)準(zhǔn)備好的菜告訴你,你再去端。這就是事件驅(qū)動(dòng)IO就緒通知的方式-epoll。
epoll的出現(xiàn),解決了select、poll的缺點(diǎn):
基于事件驅(qū)動(dòng)的方式,避免了每次都要把所有fd都掃描一遍。
epoll_wait只返回就緒的fd。
epoll使用nmap內(nèi)存映射技術(shù)避免了內(nèi)存復(fù)制的開銷。
epoll的fd數(shù)量上限是操作系統(tǒng)的大文件句柄數(shù)目,這個(gè)數(shù)目一般和內(nèi)存有關(guān),通常遠(yuǎn)大于1024。
目前,epoll是Linux2.6下最高效的IO復(fù)用方式,也是Nginx、Node的IO實(shí)現(xiàn)方式。而在freeBSD下,kqueue是另一種類似于epoll的IO復(fù)用方式。
此外,對(duì)于IO復(fù)用還有一個(gè)水平觸發(fā)和邊緣觸發(fā)的概念:
水平觸發(fā):當(dāng)就緒的fd未被用戶進(jìn)程處理后,下一次查詢依舊會(huì)返回,這是select和poll的觸發(fā)方式。
邊緣觸發(fā):無論就緒的fd是否被處理,下一次不再返回。理論上性能更高,但是實(shí)現(xiàn)相當(dāng)復(fù)雜,并且任何意外的丟失事件都會(huì)造成請(qǐng)求處理錯(cuò)誤。epoll默認(rèn)使用水平觸發(fā),通過相應(yīng)選項(xiàng)可以使用邊緣觸發(fā)。
點(diǎn)評(píng):
I/O 多路復(fù)用的特點(diǎn)是通過一種機(jī)制一個(gè)進(jìn)程能同時(shí)等待多個(gè)文件描述符,而這些文件描述符(套接字描述符)其中的任意一個(gè)進(jìn)入讀就緒狀態(tài),select()函數(shù)就可以返回。
所以, IO多路復(fù)用,本質(zhì)上不會(huì)有并發(fā)的功能,因?yàn)槿魏螘r(shí)候還是只有一個(gè)進(jìn)程或線程進(jìn)行工作,它之所以能提高效率是因?yàn)閟elect\epoll 把進(jìn)來的socket放到他們的 ‘監(jiān)視’ 列表里面,當(dāng)任何socket有可讀可寫數(shù)據(jù)立馬處理,那如果select\epoll 手里同時(shí)檢測著很多socket, 一有動(dòng)靜馬上返回給進(jìn)程處理,總比一個(gè)一個(gè)socket過來,阻塞等待,處理高效率。
當(dāng)然也可以多線程/多進(jìn)程方式,一個(gè)連接過來開一個(gè)進(jìn)程/線程處理,這樣消耗的內(nèi)存和進(jìn)程切換頁會(huì)耗掉更多的系統(tǒng)資源。
所以我們可以結(jié)合IO多路復(fù)用和多進(jìn)程/多線程 來高性能并發(fā),IO復(fù)用負(fù)責(zé)提高接受socket的通知效率,收到請(qǐng)求后,交給進(jìn)程池/線程池來處理邏輯。信號(hào)驅(qū)動(dòng)
上文的就餐方式還是需要你每次都去問一下飯菜狀況。于是,你再次不耐煩了,就跟老板說,哪個(gè)飯菜好了就通知我一聲吧。然后就自己坐在桌子那里干自己的事情。更甚者,你可以把手機(jī)號(hào)留給老板,自己出門,等飯菜好了直接發(fā)條短信給你。這就類似信號(hào)驅(qū)動(dòng)的IO模型。
流程如下:
開啟套接字信號(hào)驅(qū)動(dòng)IO功能
系統(tǒng)調(diào)用sigaction執(zhí)行信號(hào)處理函數(shù)(非阻塞,立刻返回)
數(shù)據(jù)就緒,生成sigio信號(hào),通過信號(hào)回調(diào)通知應(yīng)用來讀取數(shù)據(jù)。
此種io方式存在的一個(gè)很大的問題:Linux中信號(hào)隊(duì)列是有限制的,如果超過這個(gè)數(shù)字問題就無法讀取數(shù)據(jù)。
異步非阻塞
linux下的asynchronous IO其實(shí)用得很少。先看一下它的流程:
異步IO 流程
用戶進(jìn)程發(fā)起read操作之后,立刻就可以開始去做其它的事。而另一方面,從kernel的角度,當(dāng)它受到一個(gè)asynchronous read之后,首先它會(huì)立刻返回,所以不會(huì)對(duì)用戶進(jìn)程產(chǎn)生任何block。然后,kernel會(huì)等待數(shù)據(jù)準(zhǔn)備完成,然后將數(shù)據(jù)拷貝到用戶內(nèi)存,當(dāng)這一切都完成之后,kernel會(huì)給用戶進(jìn)程發(fā)送一個(gè)signal,告訴它read操作完成了。
阻塞IO VS 非阻塞IO:
概念:
阻塞和非阻塞關(guān)注的是程序在等待調(diào)用結(jié)果(消息,返回值)時(shí)的狀態(tài).
阻塞調(diào)用是指調(diào)用結(jié)果返回之前,當(dāng)前線程會(huì)被掛起。調(diào)用線程只有在得到結(jié)果之后才會(huì)返回。非阻塞調(diào)用指在不能立刻得到結(jié)果之前,該調(diào)用不會(huì)阻塞當(dāng)前線程。
例子:你打電話問書店老板有沒有《分布式系統(tǒng)》這本書,你如果是阻塞式調(diào)用,你會(huì)一直把自己“掛起”,直到得到這本書有沒有的結(jié)果,如果是非阻塞式調(diào)用,你不管老板有沒有告訴你,你自己先一邊去玩了, 當(dāng)然你也要偶爾過幾分鐘check一下老板有沒有返回結(jié)果。在這里阻塞與非阻塞與是否同步異步無關(guān)。跟老板通過什么方式回答你結(jié)果無關(guān)。
分析:
阻塞IO會(huì)一直block住對(duì)應(yīng)的進(jìn)程直到操作完成,而非阻塞IO在kernel還準(zhǔn)備數(shù)據(jù)的情況下會(huì)立刻返回。
同步IO VS 異步IO:
概念:
同步與異步同步和異步關(guān)注的是消息通信機(jī)制 (synchronous communication/ asynchronous communication)所謂同步,就是在發(fā)出一個(gè)調(diào)用時(shí),在沒有得到結(jié)果之前,該調(diào)用就不返回。但是一旦調(diào)用返回,就得到返回值了。換句話說,就是由調(diào)用者主動(dòng)等待這個(gè)調(diào)用的結(jié)果。而異步則是相反,調(diào)用在發(fā)出之后,這個(gè)調(diào)用就直接返回了,所以沒有返回結(jié)果。換句話說,當(dāng)一個(gè)異步過程調(diào)用發(fā)出后,調(diào)用者不會(huì)立刻得到結(jié)果。而是在調(diào)用發(fā)出后,被調(diào)用者通過狀態(tài)、通知來通知調(diào)用者,或通過回調(diào)函數(shù)處理這個(gè)調(diào)用。
典型的異步編程模型比如Node.js舉個(gè)通俗的例子:你打電話問書店老板有沒有《分布式系統(tǒng)》這本書,如果是同步通信機(jī)制,書店老板會(huì)說,你稍等,”我查一下”,然后開始查啊查,等查好了(可能是5秒,也可能是一天)告訴你結(jié)果(返回結(jié)果)。而異步通信機(jī)制,書店老板直接告訴你我查一下啊,查好了打電話給你,然后直接掛電話了(不返回結(jié)果)。然后查好了,他會(huì)主動(dòng)打電話給你。在這里老板通過“回電”這種方式來回調(diào)。
分析:
在說明同步IO和異步IO的區(qū)別之前,需要先給出兩者的定義。Stevens給出的定義(其實(shí)是POSIX的定義)是這樣子的:
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;
兩者的區(qū)別就在于同步IO做”IO operation”的時(shí)候會(huì)將process阻塞。按照這個(gè)定義,之前所述的阻塞IO,非阻塞IO ,IO復(fù)用都屬于同步IO。
有人可能會(huì)說,非阻塞IO 并沒有被block啊。這里有個(gè)非常“狡猾”的地方,定義中所指的”IO operation”是指真實(shí)的IO操作,就是例子中的recvfrom這個(gè)system call。非阻塞IO在執(zhí)行recvfrom這個(gè)system call的時(shí)候,如果kernel的數(shù)據(jù)沒有準(zhǔn)備好,這時(shí)候不會(huì)block進(jìn)程。但是,當(dāng)kernel中數(shù)據(jù)準(zhǔn)備好的時(shí)候,recvfrom會(huì)將數(shù)據(jù)從kernel拷貝到用戶內(nèi)存中,這個(gè)時(shí)候進(jìn)程是被block了,在這段時(shí)間內(nèi),進(jìn)程是被block的。
而異步IO則不一樣,當(dāng)進(jìn)程發(fā)起IO 操作之后,就直接返回再也不理睬了,直到kernel發(fā)送一個(gè)信號(hào),告訴進(jìn)程說IO完成。在這整個(gè)過程中,進(jìn)程完全沒有被block。
最后,再舉幾個(gè)不是很恰當(dāng)?shù)睦觼碚f明這四個(gè)IO Model:
有A,B,C,D四個(gè)人在釣魚:
A用的是最老式的魚竿,所以呢,得一直守著,等到魚上鉤了再拉桿;
B的魚竿有個(gè)功能,能夠顯示是否有魚上鉤,所以呢,B就和旁邊的MM聊天,隔會(huì)再看看有沒有魚上鉤,有的話就迅速拉桿;
C用的魚竿和B差不多,但他想了一個(gè)好辦法,就是同時(shí)放好幾根魚竿,然后守在旁邊,一旦有顯示說魚上鉤了,它就將對(duì)應(yīng)的魚竿拉起來;
D是個(gè)有錢人,干脆雇了一個(gè)人幫他釣魚,一旦那個(gè)人把魚釣上來了,就給D發(fā)個(gè)短信。
select,poll,epoll本質(zhì)上都是同步I/O,因?yàn)樗麄兌夹枰谧x寫事件就緒后自己負(fù)責(zé)進(jìn)行讀寫,也就是說這個(gè)讀寫過程是阻塞的
Select/Poll/Epoll 都是IO復(fù)用的實(shí)現(xiàn)方式, 上面說了使用IO復(fù)用,會(huì)把socket設(shè)置成non-blocking,然后放進(jìn)Select/Poll/Epoll 各自的監(jiān)視列表里面,那么,他們的對(duì)socket是否有數(shù)據(jù)到達(dá)的監(jiān)視機(jī)制分別是怎樣的?效率又如何?我們應(yīng)該使用哪種方式實(shí)現(xiàn)IO復(fù)用比較好?下面列出他們各自的實(shí)現(xiàn)方式,效率,優(yōu)缺點(diǎn):
(1)select,poll實(shí)現(xiàn)需要自己不斷輪詢所有fd集合,直到設(shè)備就緒,期間可能要睡眠和喚醒多次交替。而epoll其實(shí)也需要調(diào)用epoll_wait不斷輪詢就緒鏈表,期間也可能多次睡眠和喚醒交替,但是它是設(shè)備就緒時(shí),調(diào)用回調(diào)函數(shù),把就緒fd放入就緒鏈表中,并喚醒在epoll_wait中進(jìn)入睡眠的進(jìn)程。雖然都要睡眠和交替,但是select和poll在“醒著”的時(shí)候要遍歷整個(gè)fd集合,而epoll在“醒著”的時(shí)候只要判斷一下就緒鏈表是否為空就行了,這節(jié)省了大量的CPU時(shí)間。這就是回調(diào)機(jī)制帶來的性能提升。
(2)select,poll每次調(diào)用都要把fd集合從用戶態(tài)往內(nèi)核態(tài)拷貝一次,并且要把current往設(shè)備等待隊(duì)列中掛一次,而epoll只要一次拷貝,而且把current往等待隊(duì)列上掛也只掛一次(在epoll_wait的開始,注意這里的等待隊(duì)列并不是設(shè)備等待隊(duì)列,只是一個(gè)epoll內(nèi)部定義的等待隊(duì)列)。這也能節(jié)省不少的開銷。
上文講述了UNIX環(huán)境的五種IO模型?;谶@五種模型,在Java中,隨著NIO和NIO2.0(AIO)的引入,一般具有以下幾種網(wǎng)絡(luò)編程模型:
BIO
NIO
AIO
BIO是一個(gè)典型的網(wǎng)絡(luò)編程模型,是通常我們實(shí)現(xiàn)一個(gè)服務(wù)端程序的過程,步驟如下:
主線程accept請(qǐng)求阻塞
請(qǐng)求到達(dá),創(chuàng)建新的線程來處理這個(gè)套接字,完成對(duì)客戶端的響應(yīng)。
主線程繼續(xù)accept下一個(gè)請(qǐng)求
這種模型有一個(gè)很大的問題是:當(dāng)客戶端連接增多時(shí),服務(wù)端創(chuàng)建的線程也會(huì)暴漲,系統(tǒng)性能會(huì)急劇下降。因此,在此模型的基礎(chǔ)上,類似于 tomcat的bio connector,采用的是線程池來避免對(duì)于每一個(gè)客戶端都創(chuàng)建一個(gè)線程。有些地方把這種方式叫做偽異步IO(把請(qǐng)求拋到線程池中異步等待處理)。
JDK1.4開始引入了NIO類庫,這里的NIO指的是New IO,主要是使用Selector多路復(fù)用器來實(shí)現(xiàn)。Selector在Linux等主流操作系統(tǒng)上是通過epoll實(shí)現(xiàn)的。
NIO的實(shí)現(xiàn)流程,類似于select:
創(chuàng)建ServerSocketChannel監(jiān)聽客戶端連接并綁定監(jiān)聽端口,設(shè)置為非阻塞模式。
創(chuàng)建Reactor線程,創(chuàng)建多路復(fù)用器(Selector)并啟動(dòng)線程。
將ServerSocketChannel注冊(cè)到Reactor線程的Selector上。監(jiān)聽accept事件。
Selector在線程run方法中無線循環(huán)輪詢準(zhǔn)備就緒的Key。
Selector監(jiān)聽到新的客戶端接入,處理新的請(qǐng)求,完成tcp三次握手,建立物理連接。
將新的客戶端連接注冊(cè)到Selector上,監(jiān)聽讀操作。讀取客戶端發(fā)送的網(wǎng)絡(luò)消息。
客戶端發(fā)送的數(shù)據(jù)就緒則讀取客戶端請(qǐng)求,進(jìn)行處理。
相比BIO,NIO的編程非常復(fù)雜。
JDK1.7引入NIO2.0,提供了異步文件通道和異步套接字通道的實(shí)現(xiàn)。其底層在windows上是通過IOCP,在Linux上是通過epoll來實(shí)現(xiàn)的(LinuxAsynchronousChannelProvider.java,UnixAsynchronousServerSocketChannelImpl.java)。
創(chuàng)建AsynchronousServerSocketChannel,綁定監(jiān)聽端口
調(diào)用AsynchronousServerSocketChannel的accpet方法,傳入自己實(shí)現(xiàn)的CompletionHandler。包括上一步,都是非阻塞的
連接傳入,回調(diào)CompletionHandler的completed方法,在里面,調(diào)用AsynchronousSocketChannel的read方法,傳入負(fù)責(zé)處理數(shù)據(jù)的CompletionHandler。
數(shù)據(jù)就緒,觸發(fā)負(fù)責(zé)處理數(shù)據(jù)的CompletionHandler的completed方法。繼續(xù)做下一步處理即可。
寫入操作類似,也需要傳入CompletionHandler。
其編程模型相比NIO有了不少的簡化。
. | 同步阻塞IO | 偽異步IO | NIO | AIO |
---|---|---|---|---|
客戶端數(shù)目 :IO線程 | 1 : 1 | m : n | m : 1 | m : 0 |
IO模型 | 同步阻塞IO | 同步阻塞IO | 同步非阻塞IO | 異步非阻塞IO |
吞吐量 | 低 | 中 | 高 | 高 |
編程復(fù)雜度 | 簡單 | 簡單 | 非常復(fù)雜 | 復(fù)雜 |
到此,相信大家對(duì)“Java IO模型與Java網(wǎng)絡(luò)編程模型的對(duì)比”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
分享題目:JavaIO模型與Java網(wǎng)絡(luò)編程模型的對(duì)比-創(chuàng)新互聯(lián)
本文網(wǎng)址:http://www.rwnh.cn/article42/csooec.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、搜索引擎優(yōu)化、云服務(wù)器、品牌網(wǎng)站建設(shè)、微信小程序、軟件開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容