從零開始學Java 有關線程的學習總結

《Java編程思想》整理的一些學習筆記,有不對的地方,歡迎指出。 
1 .資源衝突,如果兩個線程確實是在修改同一個對象,共享資源的衝突將變得更糟糕,因爲這有可能把對象設置成不正確的狀態。通過簡單的“信號量”概念引入,把它看作是在兩個線程之間進行通信的標誌對象。如果信號量的值是零,則它監控的資源是可用的,但如果這個值是非零的,則被監控的資源不可用,所以線程必須等待。當資源可用的時候,線程增加信號量的值,然後繼續執行這個被監控的資源。把增加和減少信號量的操作定義爲原子操作,這樣就可保證兩個線程同時訪問同一資源的時候不至於衝突。 
定義一個簡化的信號量:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Semaphore</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">implements</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Invariant</span>{</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">volatile</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> semaphore = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">available</span>(){<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> semaphore==<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;}
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">acquire</span>(){ ++semaphore; }
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">release</span>(){ --semaphore; }
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> InvariantSate <span class="hljs-title" style="box-sizing: border-box;">invariant</span>(){
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> val = semaphore;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>( val==<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>||val==<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span> )
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> InvariantOk();
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> InvariantFailure(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Integer(val));
    }
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>

(其中Invariant接口在博客:線程測試框架已給出)將semaphore字段設置爲volatile ,以確保編譯器不會對任何讀取此值的操作進行優化。

2.解決共享資源競爭,之前說過,可以通過yield()和setPriority()來給線程調度機制提供建議,但這些建議未必會有多大的效果,這取決與你的具體平臺和JVM實現。Java以提供關鍵字 synchronized 的形式,爲防止資源衝突提供了內置支持。共享資源一般是以對象的形式存在的內存判斷,但也可以是文件,輸入/輸出端口,或者是打印機。要控制對共享資源的訪問,得先把它包裝進一個對象。然後把所有要訪問這個資源的方法標記爲synchronized。即一旦某個線程處於一個標記爲synchronized的方法中,那麼在這個線程從該方法返回之前,其他所有要調用類中任何標記爲synchronized方法的線程都會被阻塞。 
每個對象都含有單一的鎖(也稱爲監視器),這個鎖本身就是對象的一部分(不用寫任何特殊代碼)。當在對象上調用其任意synchronized方法的時候,此對象都被加鎖,這時該對象上的其他synchronized方法也只能等到前一個方法調用完並釋放了鎖之後才能被調用。 
針對每一個類也有一個鎖(作爲類的Class對象的一部分),所以synchronized static 方法可以在類的範圍內防止對static數據的併發訪問。

3.原子操作,即不能被線程調度機制中斷的操作;一旦操作開始,那麼它一定可以在可能發生的“上下文切換”之前(切換到其他線程執行)執行完畢。如果問題中的變量類型是除long或double以外的基本類型,對這種變量進行簡單的賦值或返回值操作的時候,纔算是原子操作。然而,只要給long或double加上volatile,操作就是原子的了。注意,在JVM中的自增加操作並不是原子操作,它牽涉到一次讀和一次寫,所以即使在這樣的簡單操作中,也爲線程出問題提供了空間。線程工作時,每個線程都可能擁有一個本地棧來維護一些變量的複本,如果把一個變量定義成volatile的,就等於告訴編譯器不要做任何優化,直接在主存操作變量。

4.保證上述問題解決,做安全的做法就是使用下面的方法: 
1)如果要對類中的某個方法進行同步控制,最好同步所有方法。如果忽略了其中一個,通常很難確定這麼做是否會有負面影響。 
2)當去除方法的同步控制時,要非常小心。通常這麼做是基於性能方面的考慮,但在JDK1.3和JDK1.4中,同步控制所需的負擔已經大大的減少。此外,只應在使用性能評價工具證實了同步控制確實是性能瓶頸的時候,才這麼做。

5.如果只是希望防止多個線程同時訪問方法內部的部分代碼而不是防止整個方法,可以使用synchronized關鍵字來分離代碼段,這種方式被稱爲“臨界區”,此時,synchronized被用來指定某個對象,此對象的鎖被用來對花括號內的代碼進行同步控制:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;">        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span>(syncObject){
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// This code can be accessed</span>
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//by only one thread at a time</span>
        }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>

使用同步控制塊,而不是對整個方法進行同步控制,可以使多個線程訪問對象的時間性能得到顯著的提高。要注意的是,當對象中的方法在不同的鎖上同步的時候,兩個線程可以訪問同一個對象:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;">class DualSynch {

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> Object syncObject = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Object();

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> synchronized <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">f</span>() {
        System.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out</span>.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Inside f()"</span>);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
            Thread.sleep(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">500</span>);
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (InterruptedException e) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RuntimeException(e);
        }
        System.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out</span>.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"leaving f()"</span>);
    }

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">g</span>() {

        synchronized (syncObject) {
            System.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out</span>.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Inside g()"</span>);
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
                Thread.sleep(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">500</span>);
            } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (InterruptedException e) {
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RuntimeException(e);
            }
            System.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out</span>.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"leaving g()"</span>);
        }
    }
}

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> SyncObject{

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">main</span>(String[] args){
        final DualSynch ds = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DualSynch();

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Thread(){
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>(){
                ds.f();
            }
        }.start();;

        ds.g();
    }
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li></ul>

DualSync對象的f()方法在this上同步(通過在整個方法同步),g()的同步控制塊在syncObject對象上同步,因此,兩個同步控制相互獨立,兩個方法同時魚腥,所以它們沒有在對象的同步控制上阻塞。因此,必須把訪問共享資源的代碼段包裝進一個合適的同步控制塊。

6.線程有四個狀態:新建、就緒、死亡、阻塞(程序能夠運行,但有某個條件阻止它運行)。進入阻塞狀態的原因: 
1)通過調用sleep(miliseconds)使線程進入休眠狀態,在指定的時間內不運行。 
2)調用wait()使線程掛起,直到線程得道了notify()或notifyAll()消息,線程纔會進入就緒狀態。 
3)線程在等待某個輸入/輸出完成。 
4)線程在某個對象上調用其同步方法,但是對象鎖不可用。

7.線程之間爲避免衝突,通過“握手機制”來進行的,這種握手可以通過Object的方法wait()和notify()來安全的實現。注意,調用sleep()的時候鎖並沒有被釋放,而調用wait()方法的確釋放了鎖,這就意味着,再調用wait()期間,可以調用線程對象中的其他同步控制方法,當一個線程在方法裏遇到了對wait()的調用的時候,線程的執行被掛起,對象上的鎖被釋放。 
wait()有兩種形式,一種與sleep()一樣接受毫秒數,不同之處: 
1)在wait()期間對象鎖是釋放的。 
2)可以通過notify()、notifyAll(),或者指令時間到期,從wait()中回覆執行。 
另一種是不帶參數的,wait()將無限等下去,知道接收到notify()或notifyAll()的消息。

8.wait()、notify()、notifyAll()這些方法是基類Object的一部分,而不是像sleep()那樣屬於Thread的一部分。因爲這些功能要用到的鎖也是所有對象的一部分,所以,你可以把wait()方法放在任何同步控制方法裏,不用考慮這個類是否繼承Thread或者實現Runnable接口。只能在同步控制方法或同步控制塊中調用wait()、notify()、notifyAll()的線程在調用這些方法前必須“擁有”(獲取)對象的鎖。(sleep不用操作鎖,所以可以在非同步控制方法裏調用)。

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span>(x){
    x.notify();
}</code>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章