選單
GSS 技術部落格
在這個園地裡我們將從技術、專案管理、客戶對談面和大家分享我們多年的經驗,希望大家不管是喜歡或是有意見,都可以回饋給我們,讓我們有機會和大家對話並一起成長!
若有任何問題請來信:gss_crm@gss.com.tw
4 分鐘閱讀時間 (837 個字)

Java Concurrency #6 - Thread Confinement

shutterstock_198004562
  • 存取 shared, mutable state 會需要處理同步,一個不需要同步的方式就是,不要 share
    • 如果 state 只從單一 Thread 存取,那就不需要同步
      • 這個技巧叫作 Thread Confinement,是達到執行緒安全最簡的方式之一
      • 如果寫過 Java Swing 桌面應用程式的人,Swing 是透過 EDT(Event Dispatch Thread) 來達到 Thread Confienment
      • 另一個範例就是 pooled JDBC 連線物件
        • 正常來說一個典型的網頁應用程式,一個 request 會是一個 Thread 去處理,該 Thread 向 connection pool 要一個 Connection
        • 承上,該 Connection 在該 Thread 用完並回傳之前,thread pool 都不會再分發給別的執行緒
        • 因此,Connection 就被侷限在該 Thread,直到所有的工作都做完為止
    • 就如同 Java 語言本身並不強制開發人員對 statelock 保護之,Thread Confinement 也只是設計中需考慮的一個環節
      • 在 Java 有 local variable 或是 ThreadLocal 可以協助達到 Thread Confinement,但那也要設計者有考量到
      • 程式設計師的工作就是要確保物件不會逃出該單一執行緒
    • 以下是幾種達到 Thread Confinement 的方式
      • 即時 Thread Confiement
        • 責任落在實作
        • 有點脆弱,因為沒有任何語言特性可以協助
        • 通常是為了實作一個特殊的子系統,如 GUI 應用程式,在帶來的好處遠大於其脆弱性時的考量
        • 盡量少用
      • Stack Confinement
        • 存取的物件本身只能透過 local variable 達成
        • local variable 天生就被侷限在執行緒內,它存活在該 thread 的 stack 上,這是其他執行緒所存取不到的
        • 又稱 within-thread/thread-local 大絕
        • 以下是一個範例:
            public int loadTheArk(Collection<Animal> candidates) {
                SortedSet<Animal> animals;
                int numPairs = 0;
                Animal candidate = null;
                animals = new TreeSet<Animal>(new SpeciesGenderComparator());
                animals.addAll(candidates);
                for (Animal a : animals) {
                    if (candidate == null || !candidate.isPotentialMate(a))
                        candidate = a;
                    else {
                        ark.load(new AnimalPair(candidate, a));
                        ++numPairs;
                        candidate = null;
                    }
                }
                return numPairs;
            }
          • 對於 primitive type 來說,你刻意想違反 stack confinement 還做不到,因為該類型的變數根本沒有參考可以取得,因此它天生麗質。但物件參考就要花一點工夫來處理了
          • 範例中 animals 是一個初始化的 TreeSet,被拿來管理一群動物,而該變數是 local variable,只有單一執行緒可以存取,只要沒人不小心把 animals 公開,就不會有問題
          • 然而這件事最好是以文件記錄,否則其他的開發人員在不知情的情況下有可能將 animals 給公開了(公開的方法如前篇所述)
      • ThreadLocal
        • 最正式的 Thread Confinement 方式
        • 每一個 Thread 都可以持有一個用來存值的物件
        • 它有 getset 方法,取得或設定該執行緒所以持有物件的值
        • 設計上適用於有 state 的 Singleton 或是 global variable,比方說一個 global 的 JDBC connection
        • 以下是透過 ThreadLocal 處理 Connection 的方法:
            private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
                public Connection initialValue() {
                    return DriverManager.getConnection(DB_URL);
                }
            };
            public static Connection getConnection() {
                return connectionHolder.get();
            }
          • 如此每一個 Thread 便能持有自己的一份 Connection
        • ThreadLocal 的使用情境是需要一個暫時的 buffer 來處理某些邏輯,並且不希望這個物件每次使用的時候都起一個新的
          • Integer.toString 在 Java 5.0 之前是透過 ThreadLocal 處理的
          • 現在則是透過 local variable char[] buf 處理
        • 執行緒第一次呼叫 ThreadLocal.get 的時候會提供一個給該執行緒專用的值
          • 概念上 ThreadLocal 就像是一個 Map<Thread, T>,雖然實際上並非這樣實作的
          • ThreadLocal 內存放的值會隨著 Thread 的結束而一併被回收(Garbage Collected)
Java Concurrency #7 - Immutability
Java Concurrency #5 - Sharing Objects[2]

相關文章

 

評論

尚無評論
已經注冊了? 這裡登入
Guest
2024/05/06, 週一

Captcha 圖像