多線程同時訪問一個對象不同的synchronized方法(驗證及解決)


Java中兩個線程不可以同時訪問同一個對象的兩個不同的synchronized方法。

多個線程訪問同一個類的synchronized方法時, 都是串行執行的 ! 就算有多個cpu也不例外 !
 synchronized方法使用了類java的內置鎖, 即鎖住的是方法所屬對象本身. 同一個鎖某個時刻只能被一個執行線程所獲取, 
因此其他線程都得等待鎖的釋放. 因此就算你有多余的cpu可以執行, 但是你沒有鎖, 所以你還是不能進入synchronized方法執行, CPU因此而空閑. 
如果某個線程長期持有一個競爭激烈的鎖, 那么將導致其他線程都因等待所的釋放而被掛起, 
從而導致CPU無法得到利用, 系統吞吐量低下. 因此要盡量避免某個線程對鎖的長期占有 !

 

復制代碼
public class SyncMethod {
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">synchronized</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> syncMethod2() {
        </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
            System.out.println(</span>"@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 已經獲取內置鎖`SyncMethod.this`)"<span style="color: #000000;">);
            Thread.sleep(</span>5000<span style="color: #000000;">);
        } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(</span>"@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 即將釋放內置鎖`SyncMethod.this`)"<span style="color: #000000;">);
    }

    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">synchronized</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> syncMethod1() {
        System.out.println(</span>"######################## (syncMethod1, 已經獲取內置鎖`SyncMethod.this`, 並即將退出)"<span style="color: #000000;">);
    }


    </span><span style="color: #0000ff;">static</span> <span style="color: #0000ff;">class</span> Thread1 <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Thread {
        SyncMethod syncMethod;

        </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> Thread1(SyncMethod syncMethod) {
            </span><span style="color: #0000ff;">this</span>.syncMethod =<span style="color: #000000;"> syncMethod;
        }

        @Override
        </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() {
            syncMethod.syncMethod2();
        }
    }

    </span><span style="color: #0000ff;">static</span> <span style="color: #0000ff;">class</span> Thread2 <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Thread {
        SyncMethod syncMethod;

        </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> Thread2(SyncMethod syncMethod) {
            </span><span style="color: #0000ff;">this</span>.syncMethod =<span style="color: #000000;"> syncMethod;
        }

        @Override
        </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() {
            System.out.println(</span>"Thread2 running ..."<span style="color: #000000;">);
            syncMethod.syncMethod1();
        }
    }

    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> main(String[] args) <span style="color: #0000ff;">throws</span><span style="color: #000000;"> InterruptedException {
        SyncMethod syncMethod </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> SyncMethod();
        Thread1 thread1 </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Thread1(syncMethod);
        Thread2 thread2 </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Thread2(syncMethod);

        thread1.start();    </span><span style="color: #008000;">//</span><span style="color: #008000;">先執行, 以便搶占鎖</span>
        Thread.sleep(500); <span style="color: #008000;">//</span><span style="color: #008000;">放棄cpu, 讓thread1執行, 以便獲的鎖</span>
thread2.start(); //在syncMethod1()方法獲得鎖時, 看看syncMethod2()方法能否執行
    <span style="color: #008000;">/*</span><span style="color: #008000;">
    能否並發執行同一個對象不同的synchronized方法, 即看看能否同時進入一個對象synchronized方法塊
    1. 創建一個有兩個synchronized方法的對象`syncMethod`
    2. 先啟動一個線程(Thread1), 並讓其進入syncMethod對象的sychronized方法(syncMethod1)內, 並使其停在synchronized方法內
    3. 再啟動一個線程(Thread2), 並執行syncMethod對象的一個synchronized方法(syncMethod2), 看看能否進入此方法內
    輸出如下: 
    @@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 已經獲取內置鎖`SyncMethod.this`)
    Thread2 running ...
    @@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 即將釋放內置鎖`SyncMethod.this`)
    ######################## (syncMethod1, 已經獲取內置鎖`SyncMethod.this`, 並即將退出)
    結果分析: 
        <strong><span style="color: #ff0000;">觀察顯示, 在輸出`Thread2 running ...`后會暫停數秒(Thread2無法獲得鎖而被掛起, 因為鎖已經被Thread1持有).</span></strong>
        如果不同線程可以同時訪問同一個對象不同synchronized方法的話, 
        在有足夠cpu時間片時(Thread1在調用syncMethod1時使用Thread.sleep放棄了cpu), 
        Thread2調用的syncMethod2方法應該馬上執行, 也就是syncMethod2方法中的語句在`Thread2 running ...`語句輸出后馬上輸出, 
        而不是等待數秒才輸出 (應為此時沒有其他線程跟Thread2競爭cpu, 況且現在的電腦都不只一個cpu), 
        因此得出結論: "不同線程不能同時執行一個對象的不同synchronized方法"
        其實此結論是顯而易見的, 原理前面已經闡明, 此處不再贅述.
    </span><span style="color: #008000;">*/</span><span style="color: #000000;">

    }
}</span></pre>
復制代碼

下面是一些關於使用鎖的一些建議:
為了避免對鎖的競爭, 你可以使用鎖分解,鎖分段以及減少線程持有鎖的時間, 如果上訴程序中的syncMethod1和syncMethod2方法是兩個不相干的方法(請求的資源不存在關系), 那么這兩個方法可以分別使用兩個不同的鎖, 改造后的SyncMethod類如下:

復制代碼
public class SyncMethod {
    private Object lock1 = new Object();
    private Object lock2 = new Object();
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> syncMethod2() {
    </span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;"> (lock1) {
        </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
            System.out.println(</span>"@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 已經獲取內置鎖`SyncMethod.this`)"<span style="color: #000000;">);
            Thread.sleep(</span>5000<span style="color: #000000;">);
        } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(</span>"@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 即將釋放內置鎖`SyncMethod.this`)"<span style="color: #000000;">);
    }
}

</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> syncMethod1() {
    </span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;"> (lock2) {
        System.out.println(</span>"######################## (syncMethod1, 已經獲取內置鎖`SyncMethod.this`, 並即將退出)"<span style="color: #000000;">);
    }
}

}

復制代碼

改造之后Thread1和Thread2就不會發生, 由於鎖競爭而掛起的情況了.

當然, 如果syncMethod1中耗時操作與鎖定的資源無關, 那么也可以將耗時操作移出同步塊. 在上述改造的基礎上對syncMethod1的進一步改造如下:

復制代碼
public void syncMethod2() {
    synchronized (lock1) {
        System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 已經獲取內置鎖`SyncMethod.this`)");
        System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 即將釋放內置鎖`SyncMethod.this`)");
    }
</span><span style="color: #008000;">//</span><span style="color: #008000;">將耗時操作移出同步塊</span>
<span style="color: #0000ff;">try</span><span style="color: #000000;"> {
    Thread.sleep(</span>5000);<span style="color: #008000;">//</span><span style="color: #008000;">與同步使用的資源無關的耗時操作</span>
} <span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
    e.printStackTrace();
}

}

復制代碼

 

轉載:https://www.cnblogs.com/Jackie-zhang/p/10384136.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM