jclown

            BlogJava :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
            3 Posts :: 0 Stories :: 6 Comments :: 0 Trackbacks

          2010年10月1日 #

            Java nio從jdk1.4開(kāi)始引入進(jìn)來(lái)的。通常聽(tīng)到比較多的說(shuō)法是: 你還沒(méi)有用nio啊,你“out”啦;nio不阻塞,傳統(tǒng)的I/O都是阻塞的,效率很低...... 如果你的系統(tǒng)在使用傳統(tǒng)I/O上已經(jīng)工作得很好且沒(méi)有更多的性能等方面的要求,千萬(wàn)不要盲目地使用nio來(lái)重構(gòu)(也許在你的系統(tǒng)的上下文環(huán)境中,使用nio所帶來(lái)其它方面的復(fù)雜性會(huì)提高,處理增多,整體效率并未提高),這至少不符合敏捷開(kāi)發(fā)的態(tài)度,簡(jiǎn)單說(shuō),不要為了nio而nio。當(dāng)然這不阻礙我們對(duì)nio的認(rèn)識(shí)。

             Java nio是在Java編程中對(duì)I/O的另外一種高級(jí)的抽象方式。其對(duì)I/O的使用方式也更加貼近操作系統(tǒng)使用I/O的方式:通道和字節(jié)緩沖。一般的用法可以參考<<Thinking in java>>。這里我們談?wù)勈褂胣io進(jìn)行網(wǎng)絡(luò)通信編程的特點(diǎn)。基本的Java socket編程對(duì)小規(guī)模的系統(tǒng)可以很好的工作,但是在資源不擴(kuò)展而用戶請(qǐng)求不斷增大的情況下,系統(tǒng)劣化的比率很大。我們通常會(huì)面臨這樣一些這些問(wèn)題:

          1、thread-per-request 的方式,這樣會(huì)有更多線程維護(hù)和切換的系統(tǒng)開(kāi)銷,同時(shí)在系統(tǒng)擴(kuò)展性方面受到限制。

          2、針對(duì)問(wèn)題1,我們可以采用線程池的方式來(lái)節(jié)省線程創(chuàng)建、維護(hù)以及切換的開(kāi)銷,但是這樣也會(huì)限制系統(tǒng)可以同時(shí)處理客戶端的數(shù)量,至少對(duì)一些長(zhǎng)連接協(xié)議來(lái)說(shuō)是這樣。此時(shí)增加線程池的大小是不能再提高系統(tǒng)性能的,而只會(huì)增加系統(tǒng)更多的線程開(kāi)銷。

          3、傳統(tǒng)的socket在讀/寫數(shù)據(jù)以及建立連接上都是阻塞式的(沒(méi)有數(shù)據(jù)可讀可能會(huì)阻塞;沒(méi)有足夠空間緩存?zhèn)鬏數(shù)臄?shù)據(jù)可能阻塞;服務(wù)端的accept方法以及socket構(gòu)造函數(shù)都會(huì)阻塞等待,直到連接建立) 。這些特性會(huì)降低整個(gè)系統(tǒng)對(duì)CPU的利用率以及系統(tǒng)的活躍性,降低系統(tǒng)對(duì)客戶請(qǐng)求的響應(yīng)性及響應(yīng)時(shí)間,在某些情況下是不可接受的甚至?xí)?lái)災(zāi)難性的結(jié)果。在這樣的情形下,我們要解決cpu利用率、線程活躍性以及提高系統(tǒng)吞吐量,我們勢(shì)必做很多額外的工作且相當(dāng)復(fù)雜。

          4、由于傳統(tǒng)socket的阻塞特性,每個(gè)對(duì)等端都在阻塞等待另外一端完成相關(guān)處理,這樣勢(shì)必增大死鎖的風(fēng)險(xiǎn)。

          5、當(dāng)然為了提高系統(tǒng)的活躍性,傳統(tǒng)的socket在accept、構(gòu)造socket以及read方法上都增加了超時(shí)控制的處理以及使用socket.close方法來(lái)打斷一些阻塞操作,而網(wǎng)絡(luò)總是不確定的,設(shè)置這些超時(shí)時(shí)間的設(shè)置在具體的上下文環(huán)境中的取值也是比較復(fù)雜的。簡(jiǎn)而言之,為了提高系統(tǒng)的活躍性會(huì)增大系統(tǒng)的復(fù)雜度。

               那么,Java nio真的就是Java socket編程解決以上若干問(wèn)題的靈丹妙藥嗎?答案是否定的。

               nio的非阻塞特性是nio最大的特點(diǎn),所謂非阻塞無(wú)非就是將通信的信道設(shè)置成為非阻塞的情況下,對(duì)該信道的所有操作都是會(huì)立即返回的。如數(shù)據(jù)讀取,沒(méi)有數(shù)據(jù)的時(shí)候會(huì)返回0而不是阻塞在信道上。這樣就增大了系統(tǒng)的活躍性,使得系統(tǒng)不必浪費(fèi)資源的I/O操作上,系統(tǒng)可以更好的利用cpu資源做一些其它處理,在某些場(chǎng)景下會(huì)提高系統(tǒng)的響應(yīng)性以及吞吐量。

               同時(shí)nio在網(wǎng)絡(luò)通信的中采用的網(wǎng)絡(luò)I/O事件驅(qū)動(dòng)的方式,即操作系統(tǒng)對(duì)用戶感興趣的通信信道及其上面的I/O事件進(jìn)行監(jiān)聽(tīng)并通知應(yīng)用程序。這實(shí)際上也是觀察者模式的一種應(yīng)用,在這樣的背景下,問(wèn)題4中的死鎖風(fēng)險(xiǎn)幾乎就沒(méi)有了。

               如果你開(kāi)發(fā)的服務(wù)端不是迭代服務(wù)器(順序化處理每個(gè)客戶請(qǐng)求),那么對(duì)于問(wèn)題1、2在nio服務(wù)器中也是一樣的。換句話說(shuō),我們開(kāi)發(fā)的并發(fā)處理服務(wù)器為了實(shí)現(xiàn)對(duì)各種網(wǎng)絡(luò)I/O事件的處理,并對(duì)每種我們所關(guān)心的網(wǎng)絡(luò)I/O事件進(jìn)行及時(shí)響應(yīng),采用thread-per-request或者線程池的方式是無(wú)法避免的。所以很多人會(huì)感覺(jué)nio的網(wǎng)絡(luò)編程結(jié)構(gòu)與非nio的網(wǎng)絡(luò)編程結(jié)構(gòu)實(shí)際是一樣的,accept換成了select,監(jiān)聽(tīng)連接入站編程了監(jiān)聽(tīng)信道上感興趣的I/O事件,為了提高系統(tǒng)的處理能力,還是要啟動(dòng)異步的線程來(lái)處理信道上的網(wǎng)絡(luò)I/O事件,只是具體實(shí)現(xiàn)不一樣而已。這說(shuō)明nio并沒(méi)有改變我們服務(wù)端程序編寫的整體結(jié)構(gòu),只是在nio的環(huán)境下,我們的確可以去提高系統(tǒng)的活躍性、響應(yīng)性和吞吐量。nio與非nio在讀/寫網(wǎng)絡(luò)數(shù)據(jù)以及連接建立等網(wǎng)絡(luò)操作上是沒(méi)有多大區(qū)別的,這些因素主要都還是取決于網(wǎng)絡(luò)狀況、操作系統(tǒng)協(xié)議棧實(shí)現(xiàn)、應(yīng)用自身處理網(wǎng)絡(luò)數(shù)據(jù)的方式等多個(gè)方面。甚至,我們?cè)谙碛胣io的同時(shí)在一定程度上還會(huì)增大了網(wǎng)絡(luò)編程的復(fù)雜度,因?yàn)閿?shù)據(jù)的讀寫以及連接的建立等操作變得更加不確定,當(dāng)然最終網(wǎng)絡(luò)編程的復(fù)雜還是取決于協(xié)議的復(fù)雜度。

          posted @ 2010-10-01 14:31 jclown 閱讀(2580) | 評(píng)論 (2)編輯 收藏

          2010年9月19日 #

               摘要: Apache MINA 以及網(wǎng)絡(luò)NIO編程剖析。旨在用MINA的實(shí)現(xiàn)來(lái)詮釋JAVA NIO 網(wǎng)路編程的深意。  閱讀全文
          posted @ 2010-09-19 21:59 jclown 閱讀(2538) | 評(píng)論 (2)編輯 收藏

                ThreadLocal是線程變量的意思,不是本地線程。它可以為線程分配特有的空間(與線程自身的堆棧不同,是線程對(duì)象在內(nèi)存中的一個(gè)Map),使得線程可以通過(guò)這個(gè)空間隨時(shí)獲取自己相關(guān)的上下文信息。

          ThreadLocal 內(nèi)幕

          ThreadLocal很容易讓使用者誤解(以前我就誤解了),因?yàn)樵谶@個(gè)類中定義了一個(gè)靜態(tài)的包級(jí)可見(jiàn)的內(nèi)部類ThreadLocalMap,同時(shí)ThreadLocal對(duì)外提供了get和set方法。那么set的時(shí)候就是把當(dāng)前線程對(duì)象作為ThreadLocalMap的key,把傳入set方法的值作為value存儲(chǔ)起來(lái);get的時(shí)候通過(guò)獲取當(dāng)前線程對(duì)象,然后到ThreadLocalMap中取得對(duì)應(yīng)的value。這種認(rèn)識(shí)是表面的很容易理解的方式,但是也是錯(cuò)誤的理解。

          每個(gè)Thread類中都有ThreadLocal.ThreadLocalMap的兩個(gè)成員,分別是threadlocals與inheritableThreadLocals。前者用來(lái)存儲(chǔ)當(dāng)前線程的相關(guān)數(shù)據(jù)信息,后者用來(lái)存儲(chǔ)從父線程中繼承或者說(shuō)拷貝過(guò)來(lái)的相關(guān)數(shù)據(jù)信息。這兩個(gè)區(qū)域也就是前面提到的線程的特別存儲(chǔ)區(qū)域,但是如何操作它們?JDK中的設(shè)計(jì)并不是讓用戶直接操作當(dāng)前線程來(lái)存取數(shù)據(jù),而是讓用戶操作ThreadLocal來(lái)對(duì)當(dāng)前線程的這兩個(gè)Map進(jìn)行存取(個(gè)人認(rèn)為這樣設(shè)計(jì)是出于安全性與易用性考慮,主要是Map中的key值需要固定才能取到同一對(duì)象),同時(shí)放置在這兩個(gè)Map中的key值就是ThreadLocal對(duì)象自身,value由用戶傳入。


          ThreadLocal 進(jìn)階

          ThreadLocal為線程獨(dú)占某些對(duì)象或者資源提供了新的手段,ThreadLocal與線程堆棧都是用來(lái)存取線程私有數(shù)據(jù)的區(qū)域,別的線程無(wú)法觸碰這兩個(gè)區(qū)域,但是二者確有不同的生命周期。這個(gè)可以這樣理解,線程跨多個(gè)方法調(diào)用的時(shí)候,方法作為Frame不斷的進(jìn)棧和出棧(方法的調(diào)用與返回),同時(shí)出棧的時(shí)候方法中的臨時(shí)變量數(shù)據(jù)會(huì)被回收,在這個(gè)線程的堆棧中消失,所以方法中的局部變量是無(wú)法在線程調(diào)用過(guò)程中穿透,除非是傳遞局部的引用類型變量,但是這也意味著方法接口都要額外接收新的參數(shù)(早期系統(tǒng)中傳遞JDBC數(shù)據(jù)庫(kù)連接的方式)。而ThreadLocal不同,準(zhǔn)確地說(shuō)是線程中的ThreadLocalMap不同,線程對(duì)象中的ThreadLocalMap對(duì)象是伴隨整個(gè)線程對(duì)象生命周期的,線程可以在任何需要的時(shí)候通過(guò)它來(lái)獲取相關(guān)數(shù)據(jù),這為線程獨(dú)占數(shù)據(jù)提供了一種更加優(yōu)雅的方式。只不過(guò)這一切都是通過(guò)ThreadLocal來(lái)操作的而已,同時(shí)ThreadLocal自身也作為不同線程中的ThreadLocalMap中的key來(lái)映射存入的value。

          ThreadLocal 應(yīng)用

          public class ThreadWriter

          {

             private static final ThreadLocal<FileWriter> local = new

          ThreadLocal<FileWriter>();

              public void write(String s)

              {

                 try

                 {

                     getFileWriter().write(s);

                 }

                 catch(IOException e)

                 {

                     //igore

                 }

              }

              private FileWriter getFileWriter()

              {

                 FileWriter fw = local.get();

                 if (null == fw)

                 {

                     try

                     {

          fw = new FileWriter(Thread.currentThread().getName() + "-info.txt", true);

                        local.set(fw);

                     }

                     catch(IOException e)

                     {

                       //igore

                     }

                 }

                 return fw;

              }

          }

          這里說(shuō)明幾個(gè)問(wèn)題:

          1、 ThreadLocal對(duì)象只有一份,它將作為不同線程中的ThreadLocalMap對(duì)象的key,所映射的值是不同文件路徑的FileWriter對(duì)象。只不過(guò)不同 線程中的ThreadLocalMap中的key值都一樣,但是因?yàn)樵诓煌腡hreadLocalMap中,不會(huì)有任何影響,反而這樣的方式為每個(gè)線程中ThreadLocalMap對(duì)象中的key值生成省去了很多功夫,這是一個(gè)很精妙的設(shè)計(jì),用戶在間接使用ThreadLocalMap的時(shí)候,不需要感知key是什么,就可以方便地存取。

          2、 一份ThreadLocal對(duì)象只能映射一種資源。如需要映射多種資源的話,定義多個(gè)ThreadLocal成員。

          3、 local.set(fw); 這句代碼表示將local自身作為key,fw作為value,存儲(chǔ)在調(diào)用這行代碼的線程中的ThreadLocalMap對(duì)象中。

          FileWriter fw = local.get(); 這句代碼表示將local自身作為key,從當(dāng)前線程的ThreadLocalMap對(duì)象中取出屬于自己獨(dú)占的FileWriter對(duì)象。 

          posted @ 2010-09-19 20:06 jclown 閱讀(2232) | 評(píng)論 (2)編輯 收藏

          主站蜘蛛池模板: 新和县| 南陵县| 剑阁县| 塘沽区| 大洼县| 镇坪县| 柘荣县| 富平县| 潜山县| 平果县| 城固县| 香港 | 扶绥县| 凤冈县| 嘉义县| 金山区| 饶平县| 潮州市| 靖安县| 桐乡市| 扎兰屯市| 宜兴市| 迁安市| 安多县| 卢氏县| 睢宁县| 申扎县| 奉化市| 栾城县| 沙坪坝区| 扶绥县| 大方县| 嵊州市| 兴安县| 盐山县| 云和县| 郧西县| 鄂州市| 平果县| 河源市| 津市市|