多線程的對象鎖和類鎖

    今天面試官問了一個問題,若是在一個類中的普通方法加上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:轉載知乎上面大牛的對類鎖和對象鎖的解釋:

做者:beralee
連接:https://www.zhihu.com/question/19708552/answer/12719903
來源:知乎
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

要明白兩個問題,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的鎖。

synchronized還有不少種使用方法,但只有明白是 那條線程得到哪一個對象的鎖,就很容易明白了
相關文章
相關標籤/搜索