1. gzyueqian
      18529173453
      首頁 > 新聞中心 > > 正文

      編寫高效的線程安全類

      更新時間: 2007-06-05 10:03:32來源: 粵嵌教育瀏覽量:586


        在語言級支持鎖定對象和線程間發信使編寫線程安全類變得簡單。本文使用簡單的編程示例來說明開發高效的線程安全類是多么有效而直觀。
        Java 編程語言為編寫多線程應用程序提供強大的語言支持。但是,編寫有用的、沒有錯誤的多線程程序仍然比較困難。本文試圖概述幾種方法,程序員可用這幾種方法來創建高效的線程安全類。
        
        并發性

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

        雖然多線程應用程序中的大多數操作都可以并行進行,但也有某些操作(如更新全局標志或處理共享文件)不能并行進行。在這些情況下,必須獲得一個鎖來防止其他線程在執行此操作的線程完成之前訪問同一個方法。在 Java 程序中,這個鎖是通過 synchronized 關鍵字提供的。清單 1 說明了它的用法。
        
        清單 1. 使用 synchronized 關鍵字來獲取鎖 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;
        }
        }
        
        這里,兩個線程不能同時調用 currentScore() 方法;當一個線程工作時,另一個線程必須阻塞。但是,可以有任意數量的線程同時通過 max() 方法訪問值,因為 max() 不是同步方法,因此它與鎖定無關。
        
        試考慮在 MaxScore 類中添加另一個方法的影響,該方法的實現如清單 2 所示。
        
        清單 2. 添加另一個方法 public synchronized void reset() {
        max = 0;
        }
        
        這個方法(當被訪問時)不僅將阻塞 reset() 方法的其他調用,而且也將阻塞 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 關鍵字既可用于方法,更一般地,也可用于任何代碼塊。清單 4 中的兩段代碼是等效的。
        
        清單 4. 等效的代碼 synchronized void f() { void f() {
        // 執行某些操作 synchronized(this) {
        } // 執行某些操作
        
         }
         }
        
        所以,為了確保 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 對象相關的鎖,因此鎖定 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 中的改寫形式是一個較好的示例,因為它只在短的時間內持有鎖,并且每次只獲得一個鎖。這樣就完全避免了當以后增加代碼時可能產生的潛在死鎖問題。
        
        同步方法的分解

        正如在前面看到的那樣,同步方法獲取對象的一個鎖。如果該方法由不同的線程頻繁調用,則此方法將成為瓶頸,因為它會對并行性造成限制,從而會對效率造成限制。這樣,作為一個一般的原則,應該盡可能地少用同步方法。盡管有這個原則,但有時一個方法可能需要完成需要鎖定一個對象幾項任務,同時還要完成相當耗時的其他任務。在這些情況下,可使用一個動態的“鎖定-釋放-鎖定-釋放”方法。例如,清單 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() 方法不需要對象被鎖定。如您所見,重寫此方法以后,對此對象的鎖在個方法完成以后被釋放,然后在第三個方法需要時重新獲得。這樣,當 write_file() 方法執行時,等待此對象的鎖的任何其他方法仍然可以運行。將同步方法分解為這種混合代碼可以明顯改善性能。但是,您需要注意不要在這種代碼中引入邏輯錯誤。
        
        嵌套類

        內部類在 Java 程序中實現了一個令人關注的概念,它允許將整個類嵌套在另一個類中。嵌套類作為包含它的類的一個成員變量。如果定期被調用的的一個特定方法需要一個類,就可以構造一個嵌套類,此嵌套類的任務就是定期調用所需的方法。這消除了對程序的其他部分的相依性,并使代碼進一步模塊化。清單 9,一個圖形時鐘的基礎,使用了內部類。
        
        清單 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() {
        // 獲取時間的系統調用
        // 重繪時鐘指針
        }
        }
        
        清單 9 中的代碼示例不靠任何其他代碼來調用 repaint() 方法。這樣,將一個時鐘并入一個較大的用戶界面就相當簡單。
        
        事件驅動處理

        當應用程序需要對事件或條件(內部的和外部的)作出反映時,有兩種方法或用來設計系統。在種方法(稱為輪詢)中,系統定期確定這一狀態并據此作出反映。這種方法(雖然簡單)也效率不高,因為您始終無法預知何時需要調用它。
        
        第二種方法(稱為事件驅動處理)效率較高,但實現起來也較為復雜。在事件驅動處理的情況下,需要一種發信機制來控制某一特定線程何時應該運行。在 Java 程序中,您可以使用 wait()、notify() 和 notifyAll() 方法向線程發送信號。這些方法允許線程在一個對象上阻塞,直到所需的條件得到滿足為止,然后再次開始運行。這種設計減少了 CPU 占用,因為線程在阻塞時不消耗執行時間.

      免費預約試聽課

      亚洲另类欧美综合久久图片区_亚洲中文字幕日产无码2020_欧美日本一区二区三区桃色视频_亚洲AⅤ天堂一区二区三区

      
      

      1. 偷拍亚洲一区一区二区三区 | 日韩欧美精品一中文字幕 | 亚洲人成网站在线观看播放 | 中文字幕色婷婷在线视频 | 午夜久久第一场电影 | 日本免费高清一区二区三区 |