<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    細(xì)心!用心!耐心!

    吾非文人,乃市井一俗人也,讀百卷書,跨江河千里,故申城一游; 一兩滴辛酸,三四年學(xué)業(yè),五六點(diǎn)粗墨,七八筆買賣,九十道人情。

    BlogJava 聯(lián)系 聚合 管理
      1 Posts :: 196 Stories :: 10 Comments :: 0 Trackbacks

    JAVA后臺(tái)程序設(shè)計(jì)及UTIL.CONCURRENT包的應(yīng)用

    JAVA后臺(tái)程序設(shè)計(jì)及UTIL.CONCURRENT包的應(yīng)用

    何 恐

    摘要 : 在很多軟件項(xiàng)目中,JAVA語言常常被用來開發(fā)后臺(tái)服務(wù)程序。線程池技術(shù)是提高這類程序性能的一個(gè)重要手段。在實(shí)踐中,該技術(shù)已經(jīng)被廣泛的使用。本文首先 對(duì)設(shè)計(jì)后臺(tái)服務(wù)程序通常需要考慮的問題進(jìn)行了基本的論述,隨后介紹了JAVA線程池的原理、使用和其他一些相關(guān)問題,最后對(duì)功能強(qiáng)大的JAVA開放源碼線 程池包util.concurrent 在實(shí)際編程中的應(yīng)用進(jìn)行了詳細(xì)介紹。
    關(guān)鍵字: JAVA;線程池;后臺(tái)服務(wù)程序;util.concurrent


    1 引言
    在軟件項(xiàng)目開發(fā)中,許多后臺(tái)服務(wù)程序的處理動(dòng)作流程都具有一個(gè)相同點(diǎn),就是:接受客戶端發(fā)來的請(qǐng)求,對(duì)請(qǐng)求進(jìn)行一些相關(guān)的處理,最后將處理結(jié)果返回給客戶 端。這些請(qǐng)求的來源和方式可能會(huì)各不相同,但是它們常常都有一個(gè)共同點(diǎn):數(shù)量巨大,處理時(shí)間短。這類服務(wù)器在實(shí)際應(yīng)用中具有較大的普遍性,如web服務(wù) 器,短信服務(wù)器,DNS服務(wù)器等等。因此,研究如何提高此類后臺(tái)程序的性能,如何保證服務(wù)器的穩(wěn)定性以及安全性都具有重要的實(shí)用價(jià)值。

    2 后臺(tái)服務(wù)程序設(shè)計(jì)
    2.1 關(guān)于設(shè)計(jì)原型
    構(gòu)建服務(wù)器應(yīng)用程序的一個(gè)簡單的模型是:啟動(dòng)一個(gè)無限循環(huán),循環(huán)里放一個(gè)監(jiān)聽線程監(jiān)聽某個(gè)地址端口。每當(dāng)一個(gè)請(qǐng)求到達(dá)就創(chuàng)建一個(gè)新線程,然后新線程為請(qǐng)求服務(wù),監(jiān)聽線程返回繼續(xù)監(jiān)聽。
    簡單舉例如下:
    import java.net.*;
    public class MyServer extends Thread{
    public void run(){
    try{
    ServerSocket server=null;
    Socket clientconnection=null;
    server = new ServerSocket(8008);//監(jiān)聽某地址端口對(duì)
    while(true){進(jìn)入無限循環(huán)
    clientconnection =server.accept();//收取請(qǐng)求
    new ServeRequest(clientconnection).start();//啟動(dòng)一個(gè)新服務(wù)線程進(jìn)行服務(wù)
    ……
    }
    }catch(Exception e){
    System.err.println("Unable to start serve listen:"+e.getMessage());
    e.printStackTrace();
    }
    }
    }
    實(shí)際上,這只是個(gè)簡單的原型,如果試圖部署以這種方式運(yùn)行的服務(wù)器應(yīng)用程序,那么這種方法的嚴(yán)重不足就很明顯。
    首先,為每個(gè)請(qǐng)求創(chuàng)建一個(gè)新線程的開銷很大,為每個(gè)請(qǐng)求創(chuàng)建新線程的服務(wù)器在創(chuàng)建和銷毀線程上花費(fèi)的時(shí)間和消耗的系統(tǒng)資源, 往往有時(shí)候要比花在處理實(shí)際的用戶請(qǐng)求的時(shí)間和資源更多。在Java中更是如此,虛擬機(jī)將試圖跟蹤每一個(gè)對(duì)象,以便能夠在對(duì)象銷毀后進(jìn)行垃圾回收。所以提 高服務(wù)程序效率的一個(gè)手段就是盡可能減少創(chuàng)建和銷毀對(duì)象的次數(shù)。這樣綜合看來,系統(tǒng)的性能瓶頸就在于線程的創(chuàng)建開銷。
    其次,除了創(chuàng)建和銷毀線程的開銷之外,活動(dòng)的線程也消耗系統(tǒng)資源。在一個(gè) JVM 里創(chuàng)建太多的線程可能會(huì)導(dǎo)致系統(tǒng)由于過度消耗內(nèi)存而用完內(nèi)存或“切換過度”。為了防止資源不足,服務(wù)器應(yīng)用程序需要一些辦法來限制任何給定時(shí)刻運(yùn)行的處理 線程數(shù)目,以防止服務(wù)器被“壓死”的情況發(fā)生。所以在設(shè)計(jì)后臺(tái)程序的時(shí)候,一般需要提前根據(jù)服務(wù)器的內(nèi)存、CPU等硬件情況設(shè)定一個(gè)線程數(shù)量的上限值。
    如果創(chuàng)建和銷毀線程的時(shí)間相對(duì)于服務(wù)時(shí)間占用的比例較大,那末假設(shè)在一個(gè)較短的時(shí)間內(nèi)有成千上萬的請(qǐng)求到達(dá),想象一下,服務(wù)器的時(shí)間和資源將會(huì)大量的花在 創(chuàng)建和銷毀線程上,而真正用于處理請(qǐng)求的時(shí)間卻相對(duì)較少,這種情況下,服務(wù)器性能瓶頸就在于創(chuàng)建和銷毀線程的時(shí)間。按照這個(gè)模型寫一個(gè)簡單的程序測(cè)試一下 即可看出,由于篇幅關(guān)系,此處略。如果把(服務(wù)時(shí)間/創(chuàng)建和銷毀線程的時(shí)間)作為衡量服務(wù)器性能的一個(gè)參數(shù),那末這個(gè)比值越大,服務(wù)器的性能就越高。
    應(yīng)此,解決此類問題的實(shí)質(zhì)就是盡量減少創(chuàng)建和銷毀線程的時(shí)間,把服務(wù)器的資源盡可能多地用到處理請(qǐng)求上來,從而發(fā)揮多線程的優(yōu)點(diǎn)(并發(fā)),避免多線程的缺點(diǎn)(創(chuàng)建和銷毀的時(shí)空開銷)。
    線程池為線程生命周期開銷問題和資源不足問題提供了解決方案。通過對(duì)多個(gè)任務(wù)重用線程,線程創(chuàng)建的開銷被分?jǐn)偟搅硕鄠€(gè)任務(wù)上。其好處是,因?yàn)樵谡?qǐng)求到達(dá)時(shí) 線程已經(jīng)存在,所以無意中也消除了線程創(chuàng)建所帶來的延遲。這樣,就可以立即為請(qǐng)求服務(wù),使應(yīng)用程序響應(yīng)更快。而且,通過適當(dāng)?shù)卣{(diào)整線程池中的線程數(shù)目,也 就是當(dāng)請(qǐng)求的數(shù)目超過某個(gè)閾值時(shí),就強(qiáng)制其它任何新到的請(qǐng)求一直等待,直到獲得一個(gè)線程來處理為止,從而可以防止資源不足。

    3    JAVA線程池原理
    3.1 原理以及實(shí)現(xiàn)
    在實(shí)踐中,關(guān)于線程池的實(shí)現(xiàn)常常有不同的方法,但是它們的基本思路大都是相似的:服務(wù)器預(yù)先存放一定數(shù)目的“熱”的線程,并發(fā)程序需要使用線程的時(shí)候,從 服務(wù)器取用一條已經(jīng)創(chuàng)建好的線程(如果線程池為空則等待),使用該線程對(duì)請(qǐng)求服務(wù),使用結(jié)束后,該線程并不刪除,而是返回線程池中,以備復(fù)用,這樣可以避 免對(duì)每一個(gè)請(qǐng)求都生成和刪除線程的昂貴操作。
    一個(gè)比較簡單的線程池至少應(yīng)包含線程池管理器、工作線程、任務(wù)隊(duì)列、任務(wù)接口等部分。其中線程池管理器(ThreadPool Manager)的作用是創(chuàng)建、銷毀并管理線程池,將工作線程放入線程池中;工作線程是一個(gè)可以循環(huán)執(zhí)行任務(wù)的線程,在沒有任務(wù)時(shí)進(jìn)行等待;任務(wù)隊(duì)列的作 用是提供一種緩沖機(jī)制,將沒有處理的任務(wù)放在任務(wù)隊(duì)列中;任務(wù)接口是每個(gè)任務(wù)必須實(shí)現(xiàn)的接口,主要用來規(guī)定任務(wù)的入口、任務(wù)執(zhí)行完后的收尾工作、任務(wù)的執(zhí) 行狀態(tài)等,工作線程通過該接口調(diào)度任務(wù)的執(zhí)行。下面的代碼實(shí)現(xiàn)了創(chuàng)建一個(gè)線程池:
    public class ThreadPool
    {
    private Stack threadpool = new Stack();
    private int poolSize;
    private int currSize=0;
    public void setSize(int n)
    {
    poolSize = n;
    }
    public void run()
    {
    for(int i=0;i

    (發(fā)帖時(shí)間:2003-11-30 11:55:56)
    --- 岑心 J

    回復(fù)(1):

    4.2    框架與結(jié)構(gòu)
    下面讓我們來看看util.concurrent的框架結(jié)構(gòu)。關(guān)于這個(gè)工具包概述的e文原版鏈接地址是http: //gee.cs.oswego.edu/dl/cpjslides/util.pdf。該工具包主要包括三大部分:同步、通道和線程池執(zhí)行器。第一部分 主要是用來定制鎖,資源管理,其他的同步用途;通道則主要是為緩沖和隊(duì)列服務(wù)的;線程池執(zhí)行器則提供了一組完善的復(fù)雜的線程池實(shí)現(xiàn)。
    --主要的結(jié)構(gòu)如下圖所示

    4.2.1 Sync
    acquire/release協(xié)議的主要接口
    - 用來定制鎖,資源管理,其他的同步用途
    - 高層抽象接口
    - 沒有區(qū)分不同的加鎖用法

    實(shí)現(xiàn)
    -Mutex, ReentrantLock, Latch, CountDown,Semaphore, WaiterPreferenceSemaphore, FIFOSemaphore, PrioritySemaphore
    還有,有幾個(gè)簡單的實(shí)現(xiàn),例如ObservableSync, LayeredSync

    舉例:如果我們要在程序中獲得一獨(dú)占鎖,可以用如下簡單方式:
    try {
    lock.acquire();
    try {
    action();
    }
    finally {
    lock.release();
    }
    }catch(Exception e){
    }

    程序中,使用lock對(duì)象的acquire()方法獲得一獨(dú)占鎖,然后執(zhí)行您的操作,鎖用完后,使用release()方法釋放之即可。呵呵,簡單吧,想 想看,如果您親自撰寫?yīng)氄兼i,大概會(huì)考慮到哪些問題?如果關(guān)鍵的鎖得不到怎末辦?用起來是不是會(huì)復(fù)雜很多?而現(xiàn)在,以往的很多細(xì)節(jié)和特殊異常情況在這里都 無需多考慮,您盡可以把精力花在解決您的應(yīng)用問題上去。

    4.2.2 通道(Channel)
    為緩沖,隊(duì)列等服務(wù)的主接口

    具體實(shí)現(xiàn)
    LinkedQueue, BoundedLinkedQueue,BoundedBuffer, BoundedPriorityQueue, SynchronousChannel, Slot

    通道例子
    class Service { // ...
    final Channel msgQ = new LinkedQueue();
    public void serve() throws InterruptedException {
    String status = doService();
    msgQ.put(status);
    }
    public Service() { // start background thread
    Runnable logger = new Runnable() {
    public void run() {
    try {
    for(;;)
    System.out.println(msqQ.take());
    }
    catch(InterruptedException ie) {} }
    };
    new Thread(logger).start();
    }
    }
    在后臺(tái)服務(wù)器中,緩沖和隊(duì)列都是最常用到的。試想,如果對(duì)所有遠(yuǎn)端的請(qǐng)求不排個(gè)隊(duì)列,讓它們一擁而上的去爭奪cpu、內(nèi)存、資源,那服務(wù)器瞬間不當(dāng)?shù)舨殴帧6谶@里,成熟的隊(duì)列和緩沖實(shí)現(xiàn)已經(jīng)提供,您只需要對(duì)其進(jìn)行正確初始化并使用即可,大大縮短了開發(fā)時(shí)間。

    4.2.3執(zhí)行器(Executor)
    Executor是這里最重要、也是我們往往最終寫程序要用到的,下面重點(diǎn)對(duì)其進(jìn)行介紹。
    類似線程的類的主接口
    - 線程池
    - 輕量級(jí)運(yùn)行框架
    - 可以定制調(diào)度算法

    只需要支持execute(Runnable r)
    - 同Thread.start類似

    實(shí)現(xiàn)
    - PooledExecutor, ThreadedExecutor, QueuedExecutor, FJTaskRunnerGroup

    PooledExecutor(線程池執(zhí)行器)是個(gè)最常用到的類,以它為例:
    可修改得屬性如下:
    - 任務(wù)隊(duì)列的類型
    - 最大線程數(shù)
    - 最小線程數(shù)
    - 預(yù)熱(預(yù)分配)和立即(分配)線程
    - 保持活躍直到工作線程結(jié)束
    -- 以后如果需要可能被一個(gè)新的代替
    - 飽和(Saturation)協(xié)議
    -- 阻塞,丟棄,生產(chǎn)者運(yùn)行,等等

    可不要小看上面這數(shù)條屬性,對(duì)這些屬性的設(shè)置完全可以等同于您自己撰寫的線程池的成百上千行代碼。下面以筆者撰寫過得一個(gè)GIS服務(wù)器為例:
    該GIS服務(wù)器是一個(gè)典型的“請(qǐng)求-服務(wù)”類型的服務(wù)器,遵循后端程序設(shè)計(jì)的一般框架。首先對(duì)所有的請(qǐng)求按照先來先服務(wù)排入一個(gè)請(qǐng)求隊(duì)列,如果瞬間到達(dá)的 請(qǐng)求超過了請(qǐng)求隊(duì)列的容量,則將溢出的請(qǐng)求轉(zhuǎn)移至一個(gè)臨時(shí)隊(duì)列。如果臨時(shí)隊(duì)列也排滿了,則對(duì)以后達(dá)到的請(qǐng)求給予一個(gè)“服務(wù)器忙”的提示后將其簡單拋棄。這 個(gè)就夠忙活一陣的了。
    然后,結(jié)合鏈表結(jié)構(gòu)實(shí)現(xiàn)一個(gè)線程池,給池一個(gè)初始容量。如果該池滿,以x2的策略將池的容量動(dòng)態(tài)增加一倍,依此類推,直到總線程數(shù)服務(wù)達(dá)到系統(tǒng)能力上限, 之后線程池容量不在增加,所有請(qǐng)求將等待一個(gè)空余的返回線程。每從池中得到一個(gè)線程,該線程就開始最請(qǐng)求進(jìn)行GIS信息的服務(wù),如取坐標(biāo)、取地圖,等等。 服務(wù)完成后,該線程返回線程池繼續(xù)為請(qǐng)求隊(duì)列離地后續(xù)請(qǐng)求服務(wù),周而復(fù)始。當(dāng)時(shí)用矢量鏈表來暫存請(qǐng)求,用wait()、 notify() 和 synchronized等原語結(jié)合矢量鏈表實(shí)現(xiàn)線程池,總共約600行程序,而且在運(yùn)行時(shí)間較長的情況下服務(wù)器不穩(wěn)定,線程池被取用的線程有異常消失的 情況發(fā)生。而使用util.concurrent相關(guān)類之后,僅用了幾十行程序就完成了相同的工作而且服務(wù)器運(yùn)行穩(wěn)定,線程池沒有丟失線程的情況發(fā)生。由 此可見util.concurrent包極大的提高了開發(fā)效率,為項(xiàng)目節(jié)省了大量的時(shí)間。
    使用PooledExecutor例子
    import java.net.*;
    /**
    *
    Title:
    *?
    Description: 負(fù)責(zé)初始化線程池以及啟動(dòng)服務(wù)器
    *
    Copyright: Copyright (c) 2003
    *
    Company:
    * @author not attributable
    * @version 1.0
    */
    public class MainServer {
    //初始化常量
    public static final int MAX_CLIENT=100; //系統(tǒng)最大同時(shí)服務(wù)客戶數(shù)
    //初始化線程池
    public static final PooledExecutor pool =
    new PooledExecutor(new BoundedBuffer(10), MAX_CLIENT); //chanel容量為10,
    //在這里為線程池初始化了一個(gè)
    //長度為10的任務(wù)緩沖隊(duì)列。

    public MainServer() {
    //設(shè)置線程池運(yùn)行參數(shù)
    pool.setMinimumPoolSize(5); //設(shè)置線程池初始容量為5個(gè)線程
    pool.discardOldestWhenBlocked();//對(duì)于超出隊(duì)列的請(qǐng)求,使用了拋棄策略。
    pool.createThreads(2); //在線程池啟動(dòng)的時(shí)候,初始化了具有一定生命周期的2個(gè)“熱”線程
    }

    public static void main(String[] args) {
    MainServer MainServer1 = new MainServer();
    new HTTPListener().start();//啟動(dòng)服務(wù)器監(jiān)聽和處理線程
    new manageServer().start();//啟動(dòng)管理線程
    }
    }



    類HTTPListener
    import java.net.*;
    /**
    *
    Title:
    *
    Description: 負(fù)責(zé)監(jiān)聽端口以及將任務(wù)交給線程池處理
    *
    Copyright: Copyright (c) 2003
    * Company:
    * @author not attributable
    * @version 1.0
    */
    public class HTTPListener extends Thread{
    public HTTPListener() {
    }
    public void run(){
    try{
    ServerSocket server=null;
    Socket clientconnection=null;
    server = new ServerSocket(8008);//服務(wù)套接字監(jiān)聽某地址端口對(duì)
    while(true){//無限循環(huán)
    clientconnection =server.accept();
    System.out.println("Client connected in!");
    //使用線程池啟動(dòng)服務(wù)
    MainServer.pool.execute(new HTTPRequest(clientconnection));//如果收到一個(gè)請(qǐng)求,則從線程池中取一個(gè)線程進(jìn)行服務(wù),任務(wù)完成后,該線程自動(dòng)返還線程池
    }
    }catch(Exception e){
    System.err.println("Unable to start serve listen:"+e.getMessage());
    e.printStackTrace();
    }
    }
    }

    關(guān)于util.concurrent工具包就有選擇的介紹到這,更詳細(xì)的信息可以閱讀這些java源代碼的API文檔。Doug Lea是個(gè)很具有“open”精神的作者,他將util.concurrent工具包的java源代碼全部公布出來,有興趣的讀者可以下載這些源代碼并細(xì) 細(xì)品味。

    5    結(jié)束語
    以上內(nèi)容介紹了線程池基本原理以及設(shè)計(jì)后臺(tái)服務(wù)程序應(yīng)考慮到的問題,并結(jié)合實(shí)例詳細(xì)介紹了重要的多線程開發(fā)工具包util.concurrent的構(gòu)架和使用。結(jié)合使用已有完善的開發(fā)包,后端服務(wù)程序的開發(fā)周期將大大縮短,同時(shí)程序性能也有了保障。



    參考文獻(xiàn)
    [1] Chad Darby,etc. 《Beginning Java Networking》. 電子工業(yè)出版社. 2002年3月.
    [2] util.concurrent 說明文件 http://gee.cs.oswego.edu/dl/cpjslides/util.pdf
    [3] 幸勇.線程池的介紹及簡單實(shí)現(xiàn).http://www-900.ibm.com/developerWorks/cn/java/l-threadPool/index.shtml
    2002年8月
    [4]BrianGoetz. 我的線程到哪里去了http://www-900.cn.ibm.com/developerworks/cn/java/j-jtp0924/index.shtml

    posted on 2007-01-31 13:52 張金鵬 閱讀(818) 評(píng)論(0)  編輯  收藏 所屬分類: 多線程編程
    主站蜘蛛池模板: 国产成人亚洲合集青青草原精品| 日韩视频免费在线| 亚洲乱码精品久久久久..| 国产精品亚洲一区二区三区 | 青娱乐在线免费观看视频| 免费观看a级毛片| 亚洲人成网亚洲欧洲无码| 蜜桃精品免费久久久久影院| 亚洲经典千人经典日产| 国产国产人免费人成免费视频| 久久精品国产亚洲AV未满十八| 免费又黄又爽的视频| 一级毛片大全免费播放| 久久亚洲精品中文字幕三区| 最近2019中文字幕免费直播| 亚洲a级片在线观看| 免费国产a国产片高清| jizz日本免费| 久久久久亚洲av无码专区导航 | 亚洲中文字幕日本无线码| 免费无码又黄又爽又刺激| 国产精品亚洲综合网站| 亚洲午夜久久久影院伊人| 19禁啪啪无遮挡免费网站| 亚洲国产欧美一区二区三区| 亚洲国产日韩成人综合天堂| 久久午夜无码免费| 亚洲日韩国产AV无码无码精品| 亚洲精品A在线观看| 99久久免费中文字幕精品| 亚洲日韩av无码中文| 国产成人亚洲影院在线观看| 在线观看免费中文视频| 亚洲精品宾馆在线精品酒店 | 四虎影库久免费视频| 久久精品一区二区免费看| 亚洲gay片在线gv网站| 亚洲av日韩av激情亚洲| 青青青国产免费一夜七次郎 | 亚洲中文字幕无码av| 久久亚洲国产精品123区|