1. 程式人生 > >Java併發程式設計初體驗(一)

Java併發程式設計初體驗(一)

一.前言
作業系統的出現使得計算機每次能執行多個執行程式,並且不同的程式都在單獨的程序中執行;作業系統為各個獨立執行的程序分配各種資源,包括記憶體、檔案控制代碼以及安全證書等。如果需要的話,在不同的程序之間可以通過一些粗粒度的通訊機制來交換資料,包括:套接字、訊號處理器、共享記憶體、訊號量以及檔案等。
之所以在計算機中加入作業系統來實現多個程式的同時執行,主要是基於以下原因:
資源利用率:在某些情況下,程式必須等待某個外部操作執行完成,例如輸入操作或輸出操作等,而在等待是程式無法執行其他任何工作。因此,如果在等待的同時可以執行另一個程式,那麼無疑將提高資源的利用率。
公平性:不同的使用者和程式對於計算機上的資源有著同等的使用權。一種高效的執行方式是通過粗粒度的時間分片(time slicing)使這些使用者和程式能夠共享計算機資源,而不是由一個程式從頭執行到尾,然後再啟動下一個程式。
便利性

:通常來說,在計算多個任務時,每個程式執行一個任務,並在必要時相互通訊,這相比一個程式執行所有任務更容易實現。
執行緒也被稱為輕量級程序,在大多數現代作業系統中,都是以執行緒為基本的排程單位,而不是程序。如果沒有明確的協同機制,那麼執行緒就將彼此獨立執行。由於同一個程序中的所有執行緒都將共享程序中的記憶體地址空間,因此這些執行緒都能訪問相同的變數並在同一個堆上分配物件,這就需要實現一種比在程序內共享資料粒度更細的資料共享機制。如果沒有明確的同步機制來協同對共享資料的訪問,那麼當一個執行緒正在使用某個變數時,另一個執行緒可能同時訪問這個變數,這將造成不可預測的後果.
二.執行緒的優勢
1.發揮多處理器的強大能力
2.建模的簡單性,每個執行緒執行一種型別的任務,相比於一個執行緒執行多個任務在程式的設計上要簡單很多
3.非同步事件的簡化處理,伺服器應用程式接受多個來自遠端客戶端的套接字連線請求時,如果為每個連線都分配其各自的執行緒並使用同步I/O,那麼會降低這些程式的開發難度。
三.執行緒帶來的風險

1.安全性問題:執行緒安全性問題是很複雜的,在沒有充足同步的情況下,多個執行緒中的操作執行順序是不可預測的,甚至會產生奇怪的結果。
2.活躍性問題:在開發併發程式碼時,一定要注意執行緒安全性時不可破壞的。安全性不僅對多執行緒程式很重要,對於單執行緒程式同樣重要。安全性的含義是永遠不發生糟糕的事情,活躍性則關注於另一個目標,某件正確的事情最終會發生。死鎖、飢餓、活鎖等都是常見的活躍性問題。
3.效能問題:與活躍性問題密切相關的是效能問題。活躍性意味著某件正確的事情最終會發生,但不夠好,因為我們希望正確的事情儘快發生。效能問題包括多個方面,比如:服務時間過長,響應不靈敏,吞吐率太低,資源消耗過高或者可伸縮性較低等。多執行緒環境中,頻繁的上下文切換(context switch)會帶來極大的開銷;共享資料的同步機制會使得記憶體快取區資料失效,抑制編譯器優化,一起多了資料同步的一些操作,這些往往都會帶來更大的效能開銷。
四.併發程式設計示例體驗

下面是一個計數器示例:有1000個執行緒,執行緒併發量設定為100,執行計數功能,看下結果

public static int num=0;
    public static int ThreadPoolNum=1000;//執行緒數量
    private static  int PREMITS=2;//執行緒併發的訊號量
    public static void main(String[] args) {
        final Semaphore semaphore = new Semaphore(PREMITS);
        ExecutorService executorService = Executors.newCachedThreadPool();//執行緒池
        for(int i=0;i<ThreadPoolNum;i++){
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        semaphore.acquire();
                        num++;
                        System.out.println("結果num:"+num);
                        semaphore.release();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        executorService.shutdown();
    }

發現每次執行結果都不一樣,比如997 998 991 …,但是基本沒看到過1000的,一起來感受一下併發程式設計吧