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

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

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

    keep moving!

    We must not cease from exploration. And the end of all our exploring will be to arrive where we began and to know the place for the first time.
    隨筆 - 37, 文章 - 2, 評(píng)論 - 3, 引用 - 0
    數(shù)據(jù)加載中……

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

    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è)簡(jiǎn)單的模型是:?jiǎn)?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)聽。
    簡(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è)簡(jiǎn)單的原型,如果試圖部署以這種方式運(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è)簡(jiǎn)單的程序測(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è)比較簡(jiǎn)單的線程池至少應(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è)簡(jiǎn)單的實(shí)現(xiàn),例如ObservableSync, LayeredSync

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

    程序中,使用lock對(duì)象的acquire()方法獲得一獨(dú)占鎖,然后執(zhí)行您的操作,鎖用完后,使用release()方法釋放之即可。呵呵,簡(jiǎn)單吧,想 想看,如果您親自撰寫?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ì)列,讓它們一擁而上的去爭(zhēng)奪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ù)器忙”的提示后將其簡(jiǎn)單拋棄。這 個(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í)間較長(zhǎng)的情況下服務(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è)
    //長(zhǎng)度為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] 幸勇.線程池的介紹及簡(jiǎn)單實(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 2008-09-07 11:10 大石頭 閱讀(381) 評(píng)論(0)  編輯  收藏 所屬分類: 多線程

    主站蜘蛛池模板: 亚洲AV无码一区二区三区鸳鸯影院| 亚洲色丰满少妇高潮18p| 91精品成人免费国产片| 亚洲天然素人无码专区| 亚洲欧洲久久av| 91老湿机福利免费体验| 亚洲AV无码专区在线电影成人| 免费在线观看一级毛片| 日韩内射激情视频在线播放免费| jiz zz在亚洲| 亚洲av女电影网| 国产网站免费观看| 日本一卡精品视频免费| 国产偷国产偷亚洲清高APP| 亚洲AV综合色区无码另类小说| 免费精品人在线二线三线区别| 成人午夜影视全部免费看| 亚洲视频在线观看视频| 在线观着免费观看国产黄| 精品亚洲av无码一区二区柚蜜| 亚洲人成网7777777国产| 国产麻豆视频免费观看| 九九九精品视频免费| 亚洲人成网站在线观看播放青青| 亚洲精品偷拍视频免费观看| 免费h片在线观看网址最新| 黄色网址大全免费| 亚洲精品美女视频| 在线亚洲人成电影网站色www| 免费精品人在线二线三线区别| 久久国产乱子伦精品免费强| 免费在线观看亚洲| 2017亚洲男人天堂一| 久久精品国产亚洲av麻豆小说| 亚洲国产精品毛片av不卡在线 | 久久久亚洲精品国产| 四虎免费久久影院| 成人A级毛片免费观看AV网站| 无码av免费一区二区三区试看| 一级成人毛片免费观看| 亚洲人成无码网站在线观看|