今天面試官問了一個問題,若是在一個類中的普通方法加上synchronized 關鍵字,兩個線程能夠同步方法這個方法嗎?爲何面試
當時回答是不能夠同步,而後面試官確認我說必定嗎?當時就知道回答錯了。多線程
如今實現了下,原來是類鎖和對象鎖的區別所在,也算是普及了下相關知識吧。ide
類鎖:就是在方法前面加上 static synchronized或者方法裏面 synchronized(MainThings.class) 這兩個寫法,那麼這是一個類鎖。this
對象鎖:就是在方法前面加上synchronized或者方法裏面 synchronized(this) 這兩個寫法,那麼這是一個類鎖。線程
結論:若是是對象鎖的話,兩個線程能夠同時訪問同一個方法。對象
若是是類鎖的話,兩個線程是不能夠同時訪問同一個方法,當前線程會將另一個線程阻塞起來,等當前線程處理完了釋放了當前鎖後才能夠進來。blog
緣由:對象鎖-》面對每個對象來講,都有各自的鎖,每一個線程訪問不一樣對象時均可以擁有本身的鎖,因此不會形成這個線程的對象被另外一個對象上鎖。get
類鎖-》對每一個線程來講,只有一把鎖,誰先擁有這把鎖,誰先訪問,其餘線程阻塞,等當前訪問完了的線程釋放鎖後,其餘線程才能夠訪問。同步
public class DoThing1 implements Runnable {io
private MainThings mainThings;
public DoThing1(MainThings mainThings)
{
this.mainThings=mainThings;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" DoThing1 is begin");
mainThings.test();
}
}
public class DoThing2 implements Runnable {
MainThings mainThings;
public DoThing2( MainThings mainThings)
{
this.mainThings=mainThings;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" DoThing2 is begin");
mainThings.test();
}
}
public class DoThing2 implements Runnable {
MainThings mainThings;
public DoThing2( MainThings mainThings)
{
this.mainThings=mainThings;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" DoThing2 is begin");
mainThings.test();
}
}
主類:
public class MainThings {
/**
* 對象鎖
*/
public synchronized void test() {
System.out.println("this is"+ Thread.currentThread().getName()+" entry");
System.out.println("this is"+ Thread.currentThread().getName()+" out");
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
MainThings mainThings=new MainThings();
MainThings mainThings2=new MainThings();
DoThing1 do1=new DoThing1(mainThings);
DoThing2 do2=new DoThing2(mainThings2);
Thread t1=new Thread(do1);
Thread t2=new Thread(do2);
t1.start();
t2.start();
}
}
執行結果:
Thread-1 DoThing2 is begin
Thread-0 DoThing1 is begin
this isThread-1 entry
this isThread-0 entry
this isThread-0 out
this isThread-1 out
類鎖:
public static synchronized void test() {
System.out.println("this is"+ Thread.currentThread().getName()+" entry");
System.out.println("this is"+ Thread.currentThread().getName()+" out");
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
MainThings mainThings=new MainThings();
MainThings mainThings2=new MainThings();
DoThing1 do1=new DoThing1(mainThings);
DoThing2 do2=new DoThing2(mainThings2);
Thread t1=new Thread(do1);
Thread t2=new Thread(do2);
t1.start();
t2.start();
}
執行結果:
Thread-0 DoThing1 is begin
this isThread-0 entry
this isThread-0 out
Thread-1 DoThing2 is begin
this isThread-1 entry
this isThread-1 out
PS:轉載知乎上面大牛的對類鎖和對象鎖的解釋:
要明白兩個問題,1.鎖的對象是誰,2.誰持有了鎖。
假設方法A和B是在同一個類Test中的兩個方法。
Test t=new Test();
t.methodB();
這個時候,methodB方法被調用時,由於加了synchronized ,須要先得到一個鎖,這個鎖的對象應該是t,也就是當前的這個Test類的實例,而得到鎖的東西是線程,也就是說當前線程拿到了t的鎖(而不是你說的B方法得到鎖),這個時候B方法內調用methodA,由於A也加了synchronized,也須要得到一個鎖,由於A和B都是Test類中的方法,因此當前線程要得到的鎖的對象也是t。因爲當前線程在執行B方法時已經持有了t對象的鎖,所以這時候調用methodA是沒有任何影響的,至關於方法A上沒有加synchronized。
另外一種狀況:假設如今有兩個Test類
Test t1=new Test();
Test t2=new Test();
t1.methodB();//此時當前線程持有了t1對象的鎖
t2.methodB();//此時當前線程也持有了t2對象的鎖
當前線程持有了兩把鎖,鎖的對象分別是兩個不一樣的Test類的實例t1和t2,互相沒有影響。
再一種狀況:假設在多線程環境下,兩個線程均可以訪問Test t=new Test();
此時假設thread1裏調用t.methodB();同時thread2裏調用t.methodB()
這時假設thread1先搶到t對象的鎖,那麼thread2須要等待thread1釋放t對象的鎖才能夠執行B方法。
結果像這樣:
thread1得到t的鎖--thread1執行methodB--thread1執行methodA--釋放t的鎖---thread2得到t的鎖--thread2執行methodB--thread2執行methodA--釋放t的鎖。