1.Synchronized修飾非靜態方法,其實是對調用該方法的對象加鎖,俗稱「對象鎖」。java
Java中每一個對象都有一個鎖,而且是惟一的。假設分配的一個對象空間,裏面有多個方法,至關於空間裏面有多個小房間,若是咱們把全部的小房間都加鎖,由於這個對象只有一把鑰匙,所以同一時間只能有一我的打開一個小房間,而後用完了還回去,再由JVM 去分配下一個得到鑰匙的人。編程
狀況1:同一個對象在兩個線程中分別訪問該對象的兩個同步方法安全
結果:會產生互斥。併發
解釋:由於鎖針對的是對象,當對象調用一個synchronized方法時,其餘同步方法須要等待其執行結束並釋放鎖後才能執行。ide
狀況2:不一樣對象在兩個線程中調用同一個同步方法測試
結果:不會產生互斥。.net
解釋:由於是兩個對象,鎖針對的是對象,並非方法,因此能夠併發執行,不會互斥。形象的來講就是由於咱們每一個線程在調用方法的時候都是new 一個對象,那麼就會出現兩個空間,兩把鑰匙,線程
2.Synchronized修飾靜態方法,其實是對該類對象加鎖,俗稱「類鎖」。對象
狀況1:用類直接在兩個線程中調用兩個不一樣的同步方法blog
結果:會產生互斥。
解釋:由於對靜態對象加鎖實際上對類(.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.靜態方法加鎖,能和全部其餘靜態方法加鎖的 進行互斥