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

Java Concurrency #1 - Concurrency?

shutterstock_198004562
  • 這篇主要是在講古,針對 Concurrent programming 講一些演進的流程,喜歡聽故事的人再續看下去吧 XD
    • 沒有 OS 的時代
      • 單一程式從頭跑到尾,收工
      • 能存取所有的硬體資源
      • 對機器寫程式很困難(組語的惡夢)
    • 有 OS 的時代(多個 process)
      • memory, file handler, security credential 共享
      • 透過 socket/signal handler/shared memory/semaphore/files 達到 IPC(Inter-prcoess communication)
      • 動機?
        • 資源分配
        • Faireness(透過 Time slicing)
        • 方便性(我要打10個 vs. 10個人做10件事)
      • 早期都是 sequential programming,但依上述動機出現了 Thread(執行緒)
        • 共享同一個 process 的 memory/file handler
        • 各自擁有 counter/stack/local variable
        • 自然而然發展成平行運算(多 CPU core 時)
        • 又稱輕量 process,可以存取同一個 JVM heap
        • 承上,這也造就了同步的問題出現
  • 多執行緒的優點
    • 善用多核 CPU
      • 在多核 CPU 的情境下,一個 Thread 可以被分配到一個 CPU Core,充分應用資源
      • 即使是單核 CPU,某些同步的 I/O 等待時間也可以去做別運算
    • 簡單化複雜的流程
      • 如果只有一類型的事要完成,那工作就很單純,處理完就好了
      • 如果有多類型的任務要完成,還得思考不同類型任務的優先順序
      • 放在執行緒的世界也是一樣的,我們可以把很多不同類型且非同步的任務,各分派一個 Thread 來達到 一個 Thread 只有一種類型的事要完成的情境
      • Servlet 或是 RMI 就是利用這種方式處理 request,使得該類型的學習曲線相對平緩。因為程式設計師不必擔心何時要開什麼 Thread 處理什麼樣類型的 request,或是何時 Socket I/O 造成 block
    • 簡單化非同步事件的處理
      • 單執行緒的程式必須以 non-blocking I/O 的方式解決一個 request 就擋住其他 request 的 I/O 的問題,但 NIO 相當複雜且易出錯
      • 多執行緒只要針對每一個 request 開一個 Thread,就可以用 synchronous I/O 了
    • User 體驗更即時
      • 早期 GUI 應用程式是單執行緒的,所以很常有按了按鈕就卡住的情形
      • 到了近代,GUI 變成多執行緒,由一個主要的 EDT(Event Dispatch Thread) 來更新 UI,其它的 worker thread 可以處理耗時的工作,如此便能讓使用者可以同時操作多項工作
  • 多執行緒的缺點
    • Thread 是個雙面刃,它透過 Java 的 memory model 達到跨平台處理並行的任務的目標,但在早期這是個進階的議題,因為資源共享會造成一些問題
    • 在沒有做同步(synchronization) 處理時,多個 Thread 的執行順序會造成結果不可預期
        public class UnsafeSequence {
            private int value;
            /** Returns a unique value. */
            public int getNext() {
                return value++;
            }
        }
      • 上面的類別只是一個簡單的序號管理,但它並非執行緒安全(Thread-safe)
      • 想像有兩個 Thread A 與 B
        • 假設一開始的 value 值是 0
        • A Thread 先取到了現在的 value
        • 接著被切換到了 B thread 也取到了現在的 value
        • 再來 value++ 分別在各自的 Thread 上進行運算,結果 A/B 兩個 Thread 都取到 value == 1,但正確的結果應該是 A 取到 1,B 取到 2,或反過來
        • 問題的發生原就在於 A 跟 B 近乎同時 讀到同一個變數,看見了同一個值,但之後的變化在彼此的 Thread 中都看不到,才會造成錯誤的結果,這就是所謂的 race condition
      • 解法很簡單,在 getNext 這個方法前面加上 synchronized 就好(目前先這樣)
    • Liveness 問題
      • 如果 safety 指的是沒有壞事會發生,liveness 指的就是最後會有好事發生
      • 但當 liveness 發生錯誤時,會使得程式既不能前進,也無法後退,可是程式依然在運行中(e.g. deadlock, livlock)
    • 效能(Performance)問題
      • Thread 會在 Context switch 的時候帶來額外的效能損耗
      • Context switch 指的是 scheduler 要記住 A Thread 的狀態,並切換到 B Thread 還原回到之前達行的狀態
      • 此外,當需要做共享資源同步的時候,編譯器不見得能優化(Optimize)這段程式,使得效能變得相對低下
  • 到處都是 Thread
    • 即使你不手動建 Thread,JVM 也至少會開幾個 Thread 來處理 Garbage CollectionFinalizing,而且你的程式也是跑在一個 Main thread
    • 如果你寫的是 Servlet 或是 RMI,執行緒更是家常便飯,每個 request 都會開一個 Thread
    • Timer,可以延後處理一些事,底層的 TimerTask 也是透過 Thread 在處理,通常會使得單純的情境變得複雜,因為如果 Timer 與其他 Thread 共用同一個資源,就有可能有風險
    • 正因為 Thread 無所不在,你更需要懂得如何讓多執行緒的程式能夠是安全無慮且正常運作的
Java Concurrency #2 - Thread Safety
Angular #38 - Directives and Pipes[4]

相關文章

 

評論

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

Captcha 圖像