Java中兩個線程不可以同時訪問同一個對象的兩個不同的synchronized方法。
多個線程訪問同一個類的synchronized方法時, 都是串行執行的 ! 就算有多個cpu也不例外 !
synchronized方法使用了類java的內置鎖, 即鎖住的是方法所屬對象本身. 同一個鎖某個時刻只能被一個執行線程所獲取,
因此其他線程都得等待鎖的釋放. 因此就算你有多余的cpu可以執行, 但是你沒有鎖, 所以你還是不能進入synchronized方法執行, CPU因此而空閑.
如果某個線程長期持有一個競爭激烈的鎖, 那么將導致其他線程都因等待所的釋放而被掛起,
從而導致CPU無法得到利用, 系統吞吐量低下. 因此要盡量避免某個線程對鎖的長期占有 !
public class SyncMethod {thread2.start(); //在syncMethod1()方法獲得鎖時, 看看syncMethod2()方法能否執行</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>
<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