Synchronzied 修飾非靜態方法==》對象鎖
Synchronzied 修飾靜態方法==》其實是類鎖,因為是靜態方法,它把整個類鎖起來了;
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 鎖效果一樣,直接屬於類的