Synchronized同步靜態方法和非靜態方法總結
1.Synchronized修飾非靜態方法,實際上是對呼叫該方法的物件加鎖,俗稱“物件鎖”。
Java中每個物件都有一個鎖,並且是唯一的。假設分配的一個物件空間,裡面有多個方法,相當於空間裡面有多個小房間,如果我們把所有的小房間都加鎖,因為這個物件只有一把鑰匙,因此同一時間只能有一個人開啟一個小房間,然後用完了還回去,再由JVM 去分配下一個獲得鑰匙的人。
情況1:同一個物件在兩個執行緒中分別訪問該物件的兩個同步方法
結果:會產生互斥。
解釋:因為鎖針對的是物件,當物件呼叫一個synchronized方法時,其他同步方法需要等待其執行結束並釋放鎖後才能執行。
情況2:不同物件在兩個執行緒中呼叫同一個同步方法
結果:不會產生互斥。
解釋:因為是兩個物件,鎖針對的是物件,並不是方法,所以可以併發執行,不會互斥。形象的來說就是因為我們每個執行緒在呼叫方法的時候都是new 一個物件,那麼就會出現兩個空間,兩把鑰匙,
2.Synchronized修飾靜態方法,實際上是對該類物件加鎖,俗稱“類鎖”。
情況1:用類直接在兩個執行緒中呼叫兩個不同的同步方法
結果:會產生互斥。
解釋:因為對靜態物件加鎖實際上對類(.class)加鎖,類物件只有一個,可以理解為任何時候都只有一個空間,裡面有N個房間,一把鎖,因此房間(同步方法)之間一定是互斥的。
注:上述情況和用單例模式宣告一個物件來呼叫非靜態方法的情況是一樣的,因為永遠就只有這一個物件。所以訪問同步方法之間一定是互斥的。
情況2:用一個類的靜態物件在兩個執行緒中呼叫靜態方法或非靜態方法
結果:會產生互斥。
解釋:因為是一個物件呼叫,同上。
情況3:一個物件在兩個執行緒中分別呼叫一個靜態同步方法和一個非靜態同步方法
結果:不會產生互斥。
解釋:因為雖然是一個物件呼叫,但是兩個方法的鎖型別不同,呼叫的靜態方法實際上是類物件在呼叫,即這兩個方法產生的並不是同一個物件鎖,因此不會互斥,會併發執行。
測試程式碼:
同步方法類:SynchronizedTest.java
public class SynchronizedTest { /*private SynchronizedTest(){} private static SynchronizedTest st; //懶漢式單例模式,執行緒不安全,需要加synchronized同步 public static SynchronizedTest getInstance(){ if(st == null){ st = new SynchronizedTest(); } return st; }*/ /*private SynchronizedTest(){} private static final SynchronizedTest st = new SynchronizedTest(); //餓漢式單利模式,天生執行緒安全 public static SynchronizedTest getInstance(){ return st; }*/ public static SynchronizedTest staticIn = new SynchronizedTest(); //靜態物件 public synchronized void method1(){ //非靜態方法1 for(int i = 0;i < 10;i++){ System.out.println("method1 is running!"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public synchronized void method2(){ //非靜態方法2 for( int i = 0; i < 10 ; i++){ System.out.println("method2 is running!"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public synchronized static void staticMethod1(){ //靜態方法1 for( int i = 0; i < 10 ; i++){ System.out.println("static method1 is running!"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public synchronized static void staticMethod2(){ //靜態方法2 for( int i = 0; i < 10 ; i++){ System.out.println("static method2 is running!"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
執行緒類1:Thread1.java(釋放不同的註釋可以測試不同的情況)
public class Thread1 implements Runnable{
@Override
public void run() {
// SynchronizedTest s = SynchronizedTest.getInstance();
// s.method1();
// SynchronizedTest s1 = new SynchronizedTest();
// s1.method1();
SynchronizedTest.staticIn.method1();
// SynchronizedTest.staticMethod1();
// SynchronizedTest.staticMethod2();
}
}
執行緒類2:Thread2.Java
public class Thread2 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
// SynchronizedTest s = SynchronizedTest.getInstance();
// SynchronizedTest s2 = new SynchronizedTest();
// s2.method1();
// s.method2();
// SynchronizedTest.staticMethod1();
// SynchronizedTest.staticMethod2();
// SynchronizedTest.staticIn.method2();
SynchronizedTest.staticIn.staticMethod1();
}
}
主類:ThreadMain.java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadMain {
public static void main(String[] args) {
Thread t1 = new Thread(new Thread1());
Thread t2 = new Thread(new Thread2());
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(t1);
exec.execute(t2);
exec.shutdown();
}
}
總結:
1.物件鎖鑰匙只能有一把才能互斥,才能保證共享變數的唯一性
2.在靜態方法上的鎖,和 例項方法上的鎖,預設不是同樣的,如果同步需要制定兩把鎖一樣。
3.關於同一個類的方法上的鎖,來自於呼叫該方法的物件,如果呼叫該方法的物件是相同的,那麼鎖必然相同,否則就不相同。比如 new A().x() 和 new A().x(),物件不同,鎖不同,如果A的單利的,就能互斥。
4.靜態方法加鎖,能和所有其他靜態方法加鎖的 進行互斥
5.靜態方法加鎖,和xx.class 鎖效果一樣,直接屬於類的