• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            woaidongmao

            文章均收錄自他人博客,但不喜標(biāo)題前加-[轉(zhuǎn)貼],因其丑陋,見諒!~
            隨筆 - 1469, 文章 - 0, 評論 - 661, 引用 - 0
            數(shù)據(jù)加載中……

            編寫高效的java線程安全類

            2000 4 01

            在語言級支持鎖定對象和線程間發(fā)信使編寫線程安全類變得簡單。本文使用簡單的編程示例來說明開發(fā)高效的線程安全類是多么有效而直觀。

            Java 編程語言為編寫多線程應(yīng)用程序提供強大的語言支持。但是,編寫有用的、沒有錯誤的多線程程序仍然比較困難。本文試圖概述幾種方法,程序員可用這幾種方法來創(chuàng)建高效的線程安全類。

            并發(fā)性

            只有當(dāng)要解決的問題需要一定程度的并發(fā)性時,程序員才會從多線程應(yīng)用程序中受益。例如,如果打印隊列應(yīng)用程序僅支持一臺打印機和一臺客戶機,則不應(yīng)該將它編寫為多線程的。一般說來,包含并發(fā)性的編碼問題通常都包含一些可以并發(fā)執(zhí)行的操作,同時也包含一些不可并發(fā)執(zhí)行的操作。例如,為多個客戶機和一個打印機提供服務(wù)的打印隊列可以支持對打印的并發(fā)請求,但向打印機的輸出必須是串行形式的。多線程實現(xiàn)還可以改善交互式應(yīng)用程序的響應(yīng)時間。

             

             

            Synchronized 關(guān)鍵字

            雖然多線程應(yīng)用程序中的大多數(shù)操作都可以并行進(jìn)行,但也有某些操作(如更新全局標(biāo)志或處理共享文件)不能并行進(jìn)行。在這些情況下,必須獲得一個鎖來防止其他線程在執(zhí)行此操作的線程完成之前訪問同一個方法。在 Java 程序中,這個鎖是通過 synchronized 關(guān)鍵字提供的。清單 1 說明了它的用法。


            清單 1. 使用 synchronized 關(guān)鍵字來獲取鎖

            public class MaxScore {
                int max;
                public MaxScore() {
                    max = 0;
                }
                public synchronized void currentScore(int s) {
                    if(s> max) {
                        max = s;
                    }
                }
                public int max() {
                    return max;
                }
            }

             

            這里,兩個線程不能同時調(diào)用 currentScore() 方法;當(dāng)一個線程工作時,另一個線程必須阻塞。但是,可以有任意數(shù)量的線程同時通過 max() 方法訪問最大值,因為 max() 不是同步方法,因此它與鎖定無關(guān)。

            試考慮在 MaxScore 類中添加另一個方法的影響,該方法的實現(xiàn)如清單 2 所示。


            清單 2. 添加另一個方法

               public synchronized void reset() {
                    max = 0;
                }

             

            這個方法(當(dāng)被訪問時)不僅將阻塞 reset() 方法的其他調(diào)用,而且也將阻塞 MaxScore 類的同一個實例中的 currentScore() 方法,因為這兩個方法都訪問同一個鎖。如果兩個方法必須不彼此阻塞,則程序員必須在更低的級別使用同步。清單 3 是另一種情況,其中兩個同步的方法可能需要彼此獨立。


            清單 3. 兩個獨立的同步方法

            import java.util.*;
            public class Jury {
                Vector members;
                Vector alternates;
                public Jury() {
                    members = new Vector(12, 1);
                    alternates = new Vector(12, 1);
                }
                public synchronized void addMember(String name) {
                    members.add(name);
                }
                public synchronized void addAlt(String name) {
                    alternates.add(name);
                }
                public synchronized Vector all() {
                    Vector retval = new Vector(members);
                    retval.addAll(alternates);
                    return retval;
                }
            }

             

            此處,兩個不同的線程可以將 members alternates 添加到 Jury 對象中。請記住, synchronized 關(guān)鍵字既可用于方法,更一般地,也可用于任何代碼塊。清單 4 中的兩段代碼是等效的。


            清單 4. 等效的代碼

            synchronized void f() {              void f() {      
                // 執(zhí)行某些操作                                              synchronized(this) {
            }                                                    // 執(zhí)行某些操作
                                                        }
                                                 }  

             

            所以,為了確保 addMember() addAlt() 方法不彼此阻塞,可按清單 5 所示重寫 Jury 類。


            清單 5. 重寫后的 Jury

            import java.util.*;
            public class Jury {
                Vector members;
                Vector alternates;
                public Jury() {
                    members = new Vector(12, 1);
                    alternates = new Vector(12, 1);
                }
                public void addMember(String name) {
                    synchronized(members) {
                        members.add(name);
                    }
                }
                public void addAlt(String name) {
                    synchronized(alternates) {
                        alternates.add(name);
                    }
                }
                public Vector all() {
                    Vector retval;
                    synchronized(members) {
                        retval = new Vector(members);
                    }
                    synchronized(alternates) {
                        retval.addAll(alternates);
                    }
                    return retval;
                }
            }

             

            請注意,我們還必須修改 all() 方法,因為對 Jury 對象同步已沒有意義。在改寫后的版本中,addMember()addAlt() all() 方法只訪問與 members alternates 對象相關(guān)的鎖,因此鎖定 Jury 對象毫無用處。另請注意,all() 方法本來可以寫為清單 6 所示的形式。


            清單 6. members alternates 用作同步的對象

               public Vector all() {
                    synchronized(members) {
                        synchronized(alternates) {
                            Vector retval;
                            retval = new Vector(members);
                            retval.addAll(alternates);
                        }
                    }
                    return retval;
                }

             

            但是,因為我們早在需要之前就獲得 members alternates 的鎖,所以這效率不高。清單 5 中的改寫形式是一個較好的示例,因為它只在最短的時間內(nèi)持有鎖,并且每次只獲得一個鎖。這樣就完全避免了當(dāng)以后增加代碼時可能產(chǎn)生的潛在死鎖問題。

             

             

            同步方法的分解

            正如在前面看到的那樣,同步方法獲取對象的一個鎖。如果該方法由不同的線程頻繁調(diào)用,則此方法將成為瓶頸,因為它會對并行性造成限制,從而會對效率造成限制。這樣,作為一個一般的原則,應(yīng)該盡可能地少用同步方法。盡管有這個原則,但有時一個方法可能需要完成需要鎖定一個對象幾項任務(wù),同時還要完成相當(dāng)耗時的其他任務(wù)。在這些情況下,可使用一個動態(tài)的鎖定-釋放-鎖定-釋放方法。例如,清單 7 和清單 8 顯示了可按這種方式變換的代碼。


            清單 7. 最初的低效率代碼

            public synchonized void doWork() {
                     unsafe1();
                write_file();
                unsafe2();
            }




            清單 8. 重寫后效率較高的代碼

            public void doWork() {
                synchonized(this) {
                             unsafe1();
                }
                write_file();
                synchonized(this) {
                    unsafe2();
                }
            }

             

            清單 7 和清單 8 假定第一個和第三個方法需要對象被鎖定,而更耗時的 write_file() 方法不需要對象被鎖定。如您所見,重寫此方法以后,對此對象的鎖在第一個方法完成以后被釋放,然后在第三個方法需要時重新獲得。這樣,當(dāng) write_file() 方法執(zhí)行時,等待此對象的鎖的任何其他方法仍然可以運行。將同步方法分解為這種混合代碼可以明顯改善性能。但是,您需要注意不要在這種代碼中引入邏輯錯誤。

             

             

            嵌套類

            內(nèi)部類在 Java 程序中實現(xiàn)了一個令人關(guān)注的概念,它允許將整個類嵌套在另一個類中。嵌套類作為包含它的類的一個成員變量。如果定期被調(diào)用的的一個特定方法需要一個類,就可以構(gòu)造一個嵌套類,此嵌套類的唯一任務(wù)就是定期調(diào)用所需的方法。這消除了對程序的其他部分的相依性,并使代碼進(jìn)一步模塊化。清單 9,一個圖形時鐘的基礎(chǔ),使用了內(nèi)部類。


            清單 9. 圖形時鐘示例

            public class Clock {
                protected class Refresher extends Thread {
                    int refreshTime;
                    public Refresher(int x) {
                        super("Refresher");
                        refreshTime = x;
                    }
                    public void run() {
                        while(true) {
                            try {
                                sleep(refreshTime);
                            }
                            catch(Exception e) {}
                            repaint();
                        }
                    }
                }
                public Clock() {
                    Refresher r = new Refresher(1000);
                    r.start();
                }
                private void repaint() {
                    // 獲取時間的系統(tǒng)調(diào)用
                    // 重繪時鐘指針
                }
            }

             

            清單 9 中的代碼示例不靠任何其他代碼來調(diào)用 repaint() 方法。這樣,將一個時鐘并入一個較大的用戶界面就相當(dāng)簡單。

             

             

            事件驅(qū)動處理

            當(dāng)應(yīng)用程序需要對事件或條件(內(nèi)部的和外部的)作出反映時,有兩種方法或用來設(shè)計系統(tǒng)。在第一種方法(稱為輪詢)中,系統(tǒng)定期確定這一狀態(tài)并據(jù)此作出反映。這種方法(雖然簡單)也效率不高,因為您始終無法預(yù)知何時需要調(diào)用它。

            第二種方法(稱為事件驅(qū)動處理)效率較高,但實現(xiàn)起來也較為復(fù)雜。在事件驅(qū)動處理的情況下,需要一種發(fā)信機制來控制某一特定線程何時應(yīng)該運行。在 Java 程序中,您可以使用 wait()notify() notifyAll() 方法向線程發(fā)送信號。這些方法允許線程在一個對象上阻塞,直到所需的條件得到滿足為止,然后再次開始運行。這種設(shè)計減少了 CPU 占用,因為線程在阻塞時不消耗執(zhí)行時間,并且可在 notify() 方法被調(diào)用時立即喚醒。與輪詢相比,事件驅(qū)動方法可以提供更短的響應(yīng)時間。

             

             

            創(chuàng)建高效的線程安全類的步驟

            編寫線程安全類的最簡單的方法是用 synchronized 聲明每個方法。雖然這種方案可以消除數(shù)據(jù)損壞,但它同時也會消除您預(yù)期從多線程獲得的任何收益。這樣,您就需要分析并確保在 synchronized 塊內(nèi)部僅占用最少的執(zhí)行時間。您必須格外關(guān)注訪問緩慢資源文件、目錄、網(wǎng)絡(luò)套接字和數(shù)據(jù)庫的方法,這些方法可能降低您的程序的效率。盡量將對這類資源的訪問放在一個單獨的線程中,最好在任何 synchronized 代碼之外。

            一個線程安全類的示例 被設(shè)計為要處理的文件的中心儲存庫。它與使用 getWork() finishWork() WorkTable 類對接的一組線程一起工作。本例旨在讓您體驗一下全功能的線程安全類,該類使用了 helper 線程和混合同步。請注意繼續(xù)添加要處理的新文件的Refresher helper 線程的用法。本例沒有調(diào)整到最佳性能,很明顯有許多地方可以改寫以改善性能,比如將 Refresher 線程改為使用 wait()/notify() 方法事件驅(qū)動的,改寫 populateTable() 方法以減少列出磁盤上的文件(這是高成本的操作)所產(chǎn)生的影響。

             

             

            小結(jié)

            通過使用可用的全部語言支持,Java 程序中的多線程編程相當(dāng)簡單。但是,使線程安全類具有較高的效率仍然比較困難。為了改善性能,您必須事先考慮并謹(jǐn)慎使用鎖定功能。

             

            參考資料

             

            關(guān)于作者

            clip_image002

             

            clip_image003

            Neel V. Kumar 是一位具有八年面向?qū)ο缶幊探?jīng)驗的軟件工程師,所用的語言為 C++ Java 編程語言。他出生在愛荷華州,目前住在加利福尼亞的 Menlo Park,剛剛涉足電信領(lǐng)域。他曾經(jīng)為許多項目提供過咨詢服務(wù),并樂意與別人分享他的知識。可以通過 neelvk@terway.com 與他聯(lián)系。

             

            posted on 2009-08-25 22:38 肥仔 閱讀(707) 評論(0)  編輯 收藏 引用 所屬分類: Web-后臺

            国内精品久久九九国产精品| 嫩草影院久久99| 国产欧美久久久精品影院| 国产精品美女久久福利网站| 久久精品亚洲日本波多野结衣| 69SEX久久精品国产麻豆| 99久久免费国产精品| 亚洲人AV永久一区二区三区久久| 欧美精品国产综合久久| 久久久久一区二区三区| 狠狠色丁香久久婷婷综合蜜芽五月| 亚洲国产精品无码久久| 精品久久人人妻人人做精品| 亚洲中文字幕无码久久精品1| 色噜噜狠狠先锋影音久久| 欧美久久久久久| 久久97久久97精品免视看秋霞| 99久久精品国产一区二区| 国产精品热久久无码av| 久久w5ww成w人免费| 一级a性色生活片久久无| 国产高潮国产高潮久久久91 | 欧美亚洲色综久久精品国产| 91麻豆精品国产91久久久久久| 无码人妻久久久一区二区三区| 欧美性大战久久久久久| 国产成人精品久久一区二区三区av| 久久久久久人妻无码| 精品久久人人爽天天玩人人妻| 久久性精品| 久久久久久久综合日本| 色综合久久久久网| 久久综合九色综合精品| 国产精品久久久久影院色| 久久久久人妻精品一区二区三区| 中文国产成人精品久久亚洲精品AⅤ无码精品 | 国产精品99久久久久久宅男| 成人国内精品久久久久一区| 午夜天堂精品久久久久| 亚洲综合日韩久久成人AV| 无码超乳爆乳中文字幕久久|