<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 189,comments - 115,trackbacks - 0
    主題:線程同步
    http://www.javaeye.com/topic/164905
    http://hxraid.javaeye.com/blog/667437
    線程同步 
    作者 : buaawhl 

    我們可以在計算機上運行各種計算機軟件程序。每一個運行的程序可能包括多個獨立運行的線程(Thread)。 
    線程(Thread)是一份獨立運行的程序,有自己專用的運行棧。線程有可能和其他線程共享一些資源,比如,內存,文件,數據庫等。 
    當多個線程同時讀寫同一份共享資源的時候,可能會引起沖突。這時候,我們需要引入線程“同步”機制,即各位線程之間要有個先來后到,不能一窩蜂擠上去搶作一團。 
    同步這個詞是從英文synchronize(使同時發生)翻譯過來的。我也不明白為什么要用這個很容易引起誤解的詞。既然大家都這么用,咱們也就只好這么將就。 
    線程同步的真實意思和字面意思恰好相反。線程同步的真實意思,其實是“排隊”:幾個線程之間要排隊,一個一個對共享資源進行操作,而不是同時進行操作。 

    因此,關于線程同步,需要牢牢記住的第一點是:線程同步就是線程排隊。同步就是排隊。線程同步的目的就是避免線程“同步”執行。這可真是個無聊的繞口令。 
    關于線程同步,需要牢牢記住的第二點是 “共享”這兩個字。只有共享資源的讀寫訪問才需要同步。如果不是共享資源,那么就根本沒有同步的必要。 
    關于線程同步,需要牢牢記住的第三點是,只有“變量”才需要同步訪問。如果共享的資源是固定不變的,那么就相當于“常量”,線程同時讀取常量也不需要同步。至少一個線程修改共享資源,這樣的情況下,線程之間就需要同步。 
    關于線程同步,需要牢牢記住的第四點是:多個線程訪問共享資源的代碼有可能是同一份代碼,也有可能是不同的代碼;無論是否執行同一份代碼,只要這些線程的代碼訪問同一份可變的共享資源,這些線程之間就需要同步。 

    為了加深理解,下面舉幾個例子。 
    有兩個采購員,他們的工作內容是相同的,都是遵循如下的步驟: 
    (1)到市場上去,尋找并購買有潛力的樣品。 
    (2)回到公司,寫報告。 
    這兩個人的工作內容雖然一樣,他們都需要購買樣品,他們可能買到同樣種類的樣品,但是他們絕對不會購買到同一件樣品,他們之間沒有任何共享資源。所以,他們可以各自進行自己的工作,互不干擾。 
    這兩個采購員就相當于兩個線程;兩個采購員遵循相同的工作步驟,相當于這兩個線程執行同一段代碼。 

    下面給這兩個采購員增加一個工作步驟。采購員需要根據公司的“布告欄”上面公布的信息,安排自己的工作計劃。 
    這兩個采購員有可能同時走到布告欄的前面,同時觀看布告欄上的信息。這一點問題都沒有。因為布告欄是只讀的,這兩個采購員誰都不會去修改布告欄上寫的信息。 

    下面增加一個角色。一個辦公室行政人員這個時候,也走到了布告欄前面,準備修改布告欄上的信息。 
    如果行政人員先到達布告欄,并且正在修改布告欄的內容。兩個采購員這個時候,恰好也到了。這兩個采購員就必須等待行政人員完成修改之后,才能觀看修改后的信息。 
    如果行政人員到達的時候,兩個采購員已經在觀看布告欄了。那么行政人員需要等待兩個采購員把當前信息記錄下來之后,才能夠寫上新的信息。 
    上述這兩種情況,行政人員和采購員對布告欄的訪問就需要進行同步。因為其中一個線程(行政人員)修改了共享資源(布告欄)。而且我們可以看到,行政人員的工作流程和采購員的工作流程(執行代碼)完全不同,但是由于他們訪問了同一份可變共享資源(布告欄),所以他們之間需要同步。 

    同步鎖 

    前面講了為什么要線程同步,下面我們就來看如何才能線程同步。 
    線程同步的基本實現思路還是比較容易理解的。我們可以給共享資源加一把鎖,這把鎖只有一把鑰匙。哪個線程獲取了這把鑰匙,才有權利訪問該共享資源。 
    生活中,我們也可能會遇到這樣的例子。一些超市的外面提供了一些自動儲物箱。每個儲物箱都有一把鎖,一把鑰匙。人們可以使用那些帶有鑰匙的儲物箱,把東西放到儲物箱里面,把儲物箱鎖上,然后把鑰匙拿走。這樣,該儲物箱就被鎖住了,其他人不能再訪問這個儲物箱。(當然,真實的儲物箱鑰匙是可以被人拿走復制的,所以不要把貴重物品放在超市的儲物箱里面。于是很多超市都采用了電子密碼鎖。) 
    線程同步鎖這個模型看起來很直觀。但是,還有一個嚴峻的問題沒有解決,這個同步鎖應該加在哪里? 
    當然是加在共享資源上了。反應快的讀者一定會搶先回答。 
    沒錯,如果可能,我們當然盡量把同步鎖加在共享資源上。一些比較完善的共享資源,比如,文件系統,數據庫系統等,自身都提供了比較完善的同步鎖機制。我們不用另外給這些資源加鎖,這些資源自己就有鎖。 
    但是,大部分情況下,我們在代碼中訪問的共享資源都是比較簡單的共享對象。這些對象里面沒有地方讓我們加鎖。 
    讀者可能會提出建議:為什么不在每一個對象內部都增加一個新的區域,專門用來加鎖呢?這種設計理論上當然也是可行的。問題在于,線程同步的情況并不是很普遍。如果因為這小概率事件,在所有對象內部都開辟一塊鎖空間,將會帶來極大的空間浪費。得不償失。 
    于是,現代的編程語言的設計思路都是把同步鎖加在代碼段上。確切的說,是把同步鎖加在“訪問共享資源的代碼段”上。這一點一定要記住,同步鎖是加在代碼段上的。 
    同步鎖加在代碼段上,就很好地解決了上述的空間浪費問題。但是卻增加了模型的復雜度,也增加了我們的理解難度。 
    現在我們就來仔細分析“同步鎖加在代碼段上”的線程同步模型。 
    首先,我們已經解決了同步鎖加在哪里的問題。我們已經確定,同步鎖不是加在共享資源上,而是加在訪問共享資源的代碼段上。 
    其次,我們要解決的問題是,我們應該在代碼段上加什么樣的鎖。這個問題是重點中的重點。這是我們尤其要注意的問題:訪問同一份共享資源的不同代碼段,應該加上同一個同步鎖;如果加的是不同的同步鎖,那么根本就起不到同步的作用,沒有任何意義。 
    這就是說,同步鎖本身也一定是多個線程之間的共享對象。 

    Java語言的synchronized關鍵字 

    為了加深理解,舉幾個代碼段同步的例子。 
    不同語言的同步鎖模型都是一樣的。只是表達方式有些不同。這里我們以當前最流行的Java語言為例。Java語言里面用synchronized關鍵字給代碼段加鎖。整個語法形式表現為 
    synchronized(同步鎖) { 
      // 訪問共享資源,需要同步的代碼段 
    } 

    這里尤其要注意的就是,同步鎖本身一定要是共享的對象。 

    … f1() { 

    Object lock1 = new Object(); // 產生一個同步鎖 

    synchronized(lock1){ 
      // 代碼段 A 
    // 訪問共享資源 resource1 
    // 需要同步 
    } 
    } 

    上面這段代碼沒有任何意義。因為那個同步鎖是在函數體內部產生的。每個線程調用這段代碼的時候,都會產生一個新的同步鎖。那么多個線程之間,使用的是不同的同步鎖。根本達不到同步的目的。 
    同步代碼一定要寫成如下的形式,才有意義。 

    public static final Object lock1 = new Object(); 

    … f1() { 

    synchronized(lock1){ // lock1 是公用同步鎖 
      // 代碼段 A 
    // 訪問共享資源 resource1 
    // 需要同步 
    } 

    你不一定要把同步鎖聲明為static或者public,但是你一定要保證相關的同步代碼之間,一定要使用同一個同步鎖。 
    講到這里,你一定會好奇,這個同步鎖到底是個什么東西。為什么隨便聲明一個Object對象,就可以作為同步鎖? 
    在Java里面,同步鎖的概念就是這樣的。任何一個Object Reference都可以作為同步鎖。我們可以把Object Reference理解為對象在內存分配系統中的內存地址。因此,要保證同步代碼段之間使用的是同一個同步鎖,我們就要保證這些同步代碼段的synchronized關鍵字使用的是同一個Object Reference,同一個內存地址。這也是為什么我在前面的代碼中聲明lock1的時候,使用了final關鍵字,這就是為了保證lock1的Object Reference在整個系統運行過程中都保持不變。 
    一些求知欲強的讀者可能想要繼續深入了解synchronzied(同步鎖)的實際運行機制。Java虛擬機規范中(你可以在google用“JVM Spec”等關鍵字進行搜索),有對synchronized關鍵字的詳細解釋。synchronized會編譯成 monitor enter, … monitor exit之類的指令對。Monitor就是實際上的同步鎖。每一個Object Reference在概念上都對應一個monitor。 
    這些實現細節問題,并不是理解同步鎖模型的關鍵。我們繼續看幾個例子,加深對同步鎖模型的理解。 

    public static final Object lock1 = new Object(); 

    … f1() { 

    synchronized(lock1){ // lock1 是公用同步鎖 
      // 代碼段 A 
    // 訪問共享資源 resource1 
    // 需要同步 
    } 
    } 

    … f2() { 

    synchronized(lock1){ // lock1 是公用同步鎖 
      // 代碼段 B 
    // 訪問共享資源 resource1 
    // 需要同步 
    } 
    } 

    上述的代碼中,代碼段A和代碼段B就是同步的。因為它們使用的是同一個同步鎖lock1。 
    如果有10個線程同時執行代碼段A,同時還有20個線程同時執行代碼段B,那么這30個線程之間都是要進行同步的。 
    這30個線程都要競爭一個同步鎖lock1。同一時刻,只有一個線程能夠獲得lock1的所有權,只有一個線程可以執行代碼段A或者代碼段B。其他競爭失敗的線程只能暫停運行,進入到該同步鎖的就緒(Ready)隊列。 
    每一個同步鎖下面都掛了幾個線程隊列,包括就緒(Ready)隊列,待召(Waiting)隊列等。比如,lock1對應的就緒隊列就可以叫做lock1 - ready queue。每個隊列里面都可能有多個暫停運行的線程。 
    注意,競爭同步鎖失敗的線程進入的是該同步鎖的就緒(Ready)隊列,而不是后面要講述的待召隊列(Waiting Queue,也可以翻譯為等待隊列)。就緒隊列里面的線程總是時刻準備著競爭同步鎖,時刻準備著運行。而待召隊列里面的線程則只能一直等待,直到等到某個信號的通知之后,才能夠轉移到就緒隊列中,準備運行。 
    成功獲取同步鎖的線程,執行完同步代碼段之后,會釋放同步鎖。該同步鎖的就緒隊列中的其他線程就繼續下一輪同步鎖的競爭。成功者就可以繼續運行,失敗者還是要乖乖地待在就緒隊列中。 
    因此,線程同步是非常耗費資源的一種操作。我們要盡量控制線程同步的代碼段范圍。同步的代碼段范圍越小越好。我們用一個名詞“同步粒度”來表示同步代碼段的范圍。 
    同步粒度 
    在Java語言里面,我們可以直接把synchronized關鍵字直接加在函數的定義上。 
    比如。 
    … synchronized … f1() { 
      // f1 代碼段 
    } 

    這段代碼就等價于 
    … f1() { 
      synchronized(this){ // 同步鎖就是對象本身 
        // f1 代碼段 
      } 
    } 

    同樣的原則適用于靜態(static)函數 
    比如。 
    … static synchronized … f1() { 
      // f1 代碼段 
    } 

    這段代碼就等價于 
    …static … f1() { 
      synchronized(Class.forName(…)){ // 同步鎖是類定義本身 
        // f1 代碼段 
      } 
    } 

    但是,我們要盡量避免這種直接把synchronized加在函數定義上的偷懶做法。因為我們要控制同步粒度。同步的代碼段越小越好。synchronized控制的范圍越小越好。 
    我們不僅要在縮小同步代碼段的長度上下功夫,我們同時還要注意細分同步鎖。 
    比如,下面的代碼 

    public static final Object lock1 = new Object(); 

    … f1() { 

    synchronized(lock1){ // lock1 是公用同步鎖 
      // 代碼段 A 
    // 訪問共享資源 resource1 
    // 需要同步 
    } 
    } 

    … f2() { 

    synchronized(lock1){ // lock1 是公用同步鎖 
      // 代碼段 B 
    // 訪問共享資源 resource1 
    // 需要同步 
    } 
    } 

    … f3() { 

    synchronized(lock1){ // lock1 是公用同步鎖 
      // 代碼段 C 
    // 訪問共享資源 resource2 
    // 需要同步 
    } 
    } 

    … f4() { 

    synchronized(lock1){ // lock1 是公用同步鎖 
      // 代碼段 D 
    // 訪問共享資源 resource2 
    // 需要同步 
    } 
    } 

    上述的4段同步代碼,使用同一個同步鎖lock1。所有調用4段代碼中任何一段代碼的線程,都需要競爭同一個同步鎖lock1。 
    我們仔細分析一下,發現這是沒有必要的。 
    因為f1()的代碼段A和f2()的代碼段B訪問的共享資源是resource1,f3()的代碼段C和f4()的代碼段D訪問的共享資源是resource2,它們沒有必要都競爭同一個同步鎖lock1。我們可以增加一個同步鎖lock2。f3()和f4()的代碼可以修改為: 
    public static final Object lock2 = new Object(); 

    … f3() { 

    synchronized(lock2){ // lock2 是公用同步鎖 
      // 代碼段 C 
    // 訪問共享資源 resource2 
    // 需要同步 
    } 
    } 

    … f4() { 

    synchronized(lock2){ // lock2 是公用同步鎖 
      // 代碼段 D 
    // 訪問共享資源 resource2 
    // 需要同步 
    } 
    } 

    這樣,f1()和f2()就會競爭lock1,而f3()和f4()就會競爭lock2。這樣,分開來分別競爭兩個鎖,就可以大大較少同步鎖競爭的概率,從而減少系統的開銷。 

    信號量 

    同步鎖模型只是最簡單的同步模型。同一時刻,只有一個線程能夠運行同步代碼。 
    有的時候,我們希望處理更加復雜的同步模型,比如生產者/消費者模型、讀寫同步模型等。這種情況下,同步鎖模型就不夠用了。我們需要一個新的模型。這就是我們要講述的信號量模型。 
    信號量模型的工作方式如下:線程在運行的過程中,可以主動停下來,等待某個信號量的通知;這時候,該線程就進入到該信號量的待召(Waiting)隊列當中;等到通知之后,再繼續運行。 
    很多語言里面,同步鎖都由專門的對象表示,對象名通常叫Monitor。 
    同樣,在很多語言中,信號量通常也有專門的對象名來表示,比如,Mutex,Semphore。 
    信號量模型要比同步鎖模型復雜許多。一些系統中,信號量甚至可以跨進程進行同步。另外一些信號量甚至還有計數功能,能夠控制同時運行的線程數。 
    我們沒有必要考慮那么復雜的模型。所有那些復雜的模型,都是最基本的模型衍生出來的。只要掌握了最基本的信號量模型——“等待/通知”模型,復雜模型也就迎刃而解了。 
    我們還是以Java語言為例。Java語言里面的同步鎖和信號量概念都非常模糊,沒有專門的對象名詞來表示同步鎖和信號量,只有兩個同步鎖相關的關鍵字——volatile和synchronized。 
    這種模糊雖然導致概念不清,但同時也避免了Monitor、Mutex、Semphore等名詞帶來的種種誤解。我們不必執著于名詞之爭,可以專注于理解實際的運行原理。 
    在Java語言里面,任何一個Object Reference都可以作為同步鎖。同樣的道理,任何一個Object Reference也可以作為信號量。 
    Object對象的wait()方法就是等待通知,Object對象的notify()方法就是發出通知。 
    具體調用方法為 
    (1)等待某個信號量的通知 
    public static final Object signal = new Object(); 

    … f1() { 
    synchronized(singal) { // 首先我們要獲取這個信號量。這個信號量同時也是一個同步鎖 

        // 只有成功獲取了signal這個信號量兼同步鎖之后,我們才可能進入這段代碼 
        signal.wait(); // 這里要放棄信號量。本線程要進入signal信號量的待召(Waiting)隊列 

    // 可憐。辛辛苦苦爭取到手的信號量,就這么被放棄了 

        // 等到通知之后,從待召(Waiting)隊列轉到就緒(Ready)隊列里面 
    // 轉到了就緒隊列中,離CPU核心近了一步,就有機會繼續執行下面的代碼了。 
    // 仍然需要把signal同步鎖競爭到手,才能夠真正繼續執行下面的代碼。命苦啊。 
        … 
    } 
    } 

    需要注意的是,上述代碼中的signal.wait()的意思。signal.wait()很容易導致誤解。signal.wait()的意思并不是說,signal開始wait,而是說,運行這段代碼的當前線程開始wait這個signal對象,即進入signal對象的待召(Waiting)隊列。 

    (2)發出某個信號量的通知 
    … f2() { 
    synchronized(singal) { // 首先,我們同樣要獲取這個信號量。同時也是一個同步鎖。 

        // 只有成功獲取了signal這個信號量兼同步鎖之后,我們才可能進入這段代碼 
    signal.notify(); // 這里,我們通知signal的待召隊列中的某個線程。 

    // 如果某個線程等到了這個通知,那個線程就會轉到就緒隊列中 
    // 但是本線程仍然繼續擁有signal這個同步鎖,本線程仍然繼續執行 
    // 嘿嘿,雖然本線程好心通知其他線程, 
    // 但是,本線程可沒有那么高風亮節,放棄到手的同步鎖 
    // 本線程繼續執行下面的代碼 
        … 
    } 
    } 

    需要注意的是,signal.notify()的意思。signal.notify()并不是通知signal這個對象本身。而是通知正在等待signal信號量的其他線程。 

    以上就是Object的wait()和notify()的基本用法。 
    實際上,wait()還可以定義等待時間,當線程在某信號量的待召隊列中,等到足夠長的時間,就會等無可等,無需再等,自己就從待召隊列轉移到就緒隊列中了。 
    另外,還有一個notifyAll()方法,表示通知待召隊列里面的所有線程。 
    這些細節問題,并不對大局產生影響。 

    綠色線程 

    綠色線程(Green Thread)是一個相對于操作系統線程(Native Thread)的概念。 
    操作系統線程(Native Thread)的意思就是,程序里面的線程會真正映射到操作系統的線程,線程的運行和調度都是由操作系統控制的 
    綠色線程(Green Thread)的意思是,程序里面的線程不會真正映射到操作系統的線程,而是由語言運行平臺自身來調度。 
    當前版本的Python語言的線程就可以映射到操作系統線程。當前版本的Ruby語言的線程就屬于綠色線程,無法映射到操作系統的線程,因此Ruby語言的線程的運行速度比較慢。 
    難道說,綠色線程要比操作系統線程要慢嗎?當然不是這樣。事實上,情況可能正好相反。Ruby是一個特殊的例子。線程調度器并不是很成熟。 
    目前,線程的流行實現模型就是綠色線程。比如,stackless Python,就引入了更加輕量的綠色線程概念。在線程并發編程方面,無論是運行速度還是并發負載上,都優于Python。 
    另一個更著名的例子就是ErLang(愛立信公司開發的一種開源語言)。 
    ErLang的綠色線程概念非常徹底。ErLang的線程不叫Thread,而是叫做Process。這很容易和進程混淆起來。這里要注意區分一下。 
    ErLang Process之間根本就不需要同步。因為ErLang語言的所有變量都是final的,不允許變量的值發生任何變化。因此根本就不需要同步。 
    final變量的另一個好處就是,對象之間不可能出現交叉引用,不可能構成一種環狀的關聯,對象之間的關聯都是單向的,樹狀的。因此,內存垃圾回收的算法效率也非常高。這就讓ErLang能夠達到Soft Real Time(軟實時)的效果。這對于一門支持內存垃圾回收的語言來說,可不是一件容易的事情。 
    posted on 2010-12-01 20:38 MEYE 閱讀(684) 評論(1)  編輯  收藏 所屬分類: Android3D

    FeedBack:
    # re: 主題:線程同步
    2010-12-16 13:20 | 論文寫作發表
    專業代發代寫職稱學術畢業論文(長期有效)QQ:809460389


    QQ:809460389(論文寫作發表) 網址:www.18lunwen.cn
    ◆推薦發表:各類職稱及學術論文!本工作室擁有大量長期合作期刊,已有6年成功運作經驗. 。
    ◆代寫:博士、 MBA、.MPA 、碩士、本科、專科論文、外語論文,課程論文,職稱論文、畢業論文、學術論文(經濟管理、財會、教育、計算機、醫學、建筑、科技、化工、物流、水運、文學、檔案、社科等論文)。
    ◆論文推薦公開刊物發表(省級/國家級/核心/CN/ISSN/ISBN)。我處提供的期刊雜志均是經國家新聞出版署批準;同時具有CN刊號和ISSN刊號公開出版的期刊,均可以在官方渠道查詢。所有刊物都可以通過數字化期刊群、CNKI中國期刊網權威檢索機構驗證。
    ◆本工作室擁有強大的高學歷專業寫手、發表隊伍為您服務,以打造完美論文為目的,為您提供寫作及發表一條龍服務,解決您的后顧之憂。讓我們以卓越的信譽,強大的專家隊伍,報刊出版界廣泛的合作關系運用生花妙筆為您成功輝煌盡心盡力。
    E-mail:lunwen5180@sina.com .(一到兩個工作日內回復,投稿時請注明期刊名稱,作者姓名和聯系電話等信息)
    咨詢電話:13479894956 0792-4827209 工作QQ:809460389 (論文寫作發表) 來稿務必注明作者姓名、單位、通訊地址、郵編、聯系電話及電子信箱,以便回寄樣刊
      回復  更多評論
      
    主站蜘蛛池模板: 亚洲区小说区图片区QVOD| 亚洲国产午夜中文字幕精品黄网站| 国产大陆亚洲精品国产| 噜噜噜亚洲色成人网站∨| 久久久久无码专区亚洲av| 国产精品免费视频一区| 午夜国产精品免费观看 | 2021在线永久免费视频| 三年片免费观看大全国语| 曰批全过程免费视频免费看| 亚洲一区二区三区在线观看网站 | 国产成人无码免费看片软件| 亚洲真人无码永久在线观看| 亚洲色图.com| 久久精品夜色国产亚洲av| 成人免费福利视频| 男女一边桶一边摸一边脱视频免费| 亚洲av色香蕉一区二区三区| 亚洲同性男gay网站在线观看| 91久久亚洲国产成人精品性色| 亚洲日韩国产精品第一页一区| 亚洲国产精品自产在线播放| 深夜国产福利99亚洲视频| 四虎永久免费影院在线| 白白国产永久免费视频| 免费无码看av的网站| 处破痛哭A√18成年片免费| 成年女人喷潮毛片免费播放| 成人午夜性A级毛片免费| 大地资源二在线观看免费高清| 国产人在线成免费视频| 国产片AV片永久免费观看| 青青草a免费线观a| 毛片视频免费观看| 精品无码国产污污污免费| 国产高清免费在线| 国产精品免费_区二区三区观看| 国产成人aaa在线视频免费观看| 国产三级免费电影| 亚洲精品无码激情AV| 国产成人精品日本亚洲专区61|