jclown

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

          2010年10月1日 #

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

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

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

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

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

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

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

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

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

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

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

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

          2010年9月19日 #

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

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

          ThreadLocal 內(nèi)幕

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

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


          ThreadLocal 進(jìn)階

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

              }

          }

          這里說明幾個(gè)問題:

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

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

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

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

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

          主站蜘蛛池模板: 拜泉县| 板桥市| 旅游| 肥西县| 长丰县| 新竹县| 满洲里市| 北海市| 吉木乃县| 吉林市| 新密市| 揭东县| 会宁县| 哈密市| 新竹市| 海晏县| 舟曲县| 高陵县| 松滋市| 安溪县| 舟山市| 玛曲县| 安仁县| 浙江省| 扬中市| 仁怀市| 洞头县| 桂林市| 藁城市| 湛江市| 裕民县| 屏南县| 郁南县| 同心县| 永济市| 内乡县| 重庆市| 大关县| 江孜县| 白银市| 芦山县|