在Java中徐钠,synchronized關(guān)鍵字是用來控制線程同步的值依,就是在多線程的環(huán)境下良狈,控制synchronized代碼段不被多個(gè)線程同時(shí)執(zhí)行昭卓。synchronized既可以加在一段代碼上张足,也可以加在方法上触创。
關(guān)鍵是,不要認(rèn)為給方法或者代碼段加上synchronized就萬事大吉为牍,看下面一段代碼:
class Sync {
public synchronized void test() {
System.out.println("test開始..");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test結(jié)束..");
}
}
class MyThread extends Thread {
public void run() {
Sync sync = new Sync();
sync.test();
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
Thread thread = new MyThread();
thread.start();
}
}
}
運(yùn)行結(jié)果:
test開始..
test開始..
test開始..
test結(jié)束..
test結(jié)束..
test結(jié)束..
可以看出來哼绑,上面的程序起了三個(gè)線程,同時(shí)運(yùn)行Sync類中的test()方法碉咆,雖然test()方法加上了synchronized抖韩,但是還是同時(shí)運(yùn)行起來,貌似synchronized沒起作用疫铜。
將test()方法上的synchronized去掉茂浮,在方法內(nèi)部加上synchronized(this):
public void test() {
synchronized(this){
System.out.println("test開始..");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test結(jié)束..");
}
}
運(yùn)行結(jié)果:
test開始..
test開始..
test開始..
test結(jié)束..
test結(jié)束..
test結(jié)束..
一切還是這么平靜,沒有看到synchronized起到作用壳咕。
實(shí)際上席揽,synchronized(this)以及非static的synchronized方法(至于static synchronized方法請往下看),只能防止多個(gè)線程同時(shí)執(zhí)行同一個(gè)對象的同步代碼段谓厘。
回到本文的題目上:synchronized鎖住的是代碼還是對象幌羞。答案是:synchronized鎖住的是括號里的對象,而不是代碼竟稳。對于非static的synchronized方法属桦,鎖的就是對象本身也就是this熊痴。
當(dāng)synchronized鎖住一個(gè)對象后,別的線程如果也想拿到這個(gè)對象的鎖聂宾,就必須等待這個(gè)線程執(zhí)行完成釋放鎖愁拭,才能再次給對象加鎖,這樣才達(dá)到線程同步的目的亏吝。即使兩個(gè)不同的代碼段岭埠,都要鎖同一個(gè)對象,那么這兩個(gè)代碼段也不能在多線程環(huán)境下同時(shí)運(yùn)行蔚鸥。
所以我們在用synchronized關(guān)鍵字的時(shí)候惜论,能縮小代碼段的范圍就盡量縮小,能在代碼段上加同步就不要再整個(gè)方法上加同步止喷。這叫減小鎖的粒度馆类,使代碼更大程度的并發(fā)。原因是基于以上的思想弹谁,鎖的代碼段太長了乾巧,別的線程是不是要等很久,等的花兒都謝了预愤。當(dāng)然這段是題外話沟于,與本文核心思想并無太大關(guān)聯(lián)。
再看上面的代碼植康,每個(gè)線程中都new了一個(gè)Sync類的對象旷太,也就是產(chǎn)生了三個(gè)Sync對象,由于不是同一個(gè)對象销睁,所以可以多線程同時(shí)運(yùn)行synchronized方法或代碼段供璧。
為了驗(yàn)證上述的觀點(diǎn),修改一下代碼冻记,讓三個(gè)線程使用同一個(gè)Sync的對象睡毒。
class MyThread extends Thread {
private Sync sync;
public MyThread(Sync sync) {
this.sync = sync;
}
public void run() {
sync.test();
}
}
public class Main {
public static void main(String[] args) {
Sync sync = new Sync();
for (int i = 0; i < 3; i++) {
Thread thread = new MyThread(sync);
thread.start();
}
}
}
運(yùn)行結(jié)果:
test開始..
test結(jié)束..
test開始..
test結(jié)束..
test開始..
test結(jié)束..
可以看到,此時(shí)的synchronized就起了作用冗栗。
那么演顾,如果真的想鎖住這段代碼,要怎么做贞瞒?也就是偶房,如果還是最開始的那段代碼趁曼,每個(gè)線程new一個(gè)Sync對象军浆,怎么才能讓test方法不會被多線程執(zhí)行。
解決也很簡單挡闰,只要鎖住同一個(gè)對象不就行了乒融。例如掰盘,synchronized后的括號中鎖同一個(gè)固定對象,這樣就行了赞季。這樣是沒問題愧捕,但是,比較多的做法是讓synchronized鎖這個(gè)類對應(yīng)的Class對象申钩。
class Sync {
public void test() {
synchronized (Sync.class) {
System.out.println("test開始..");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test結(jié)束..");
}
}
}
class MyThread extends Thread {
public void run() {
Sync sync = new Sync();
sync.test();
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
Thread thread = new MyThread();
thread.start();
}
}
}
運(yùn)行結(jié)果:
test開始..
test結(jié)束..
test開始..
test結(jié)束..
test開始..
test結(jié)束..
上面代碼用synchronized(Sync.class)實(shí)現(xiàn)了全局鎖的效果次绘。
最后說說static synchronized方法,static方法可以直接類名加方法名調(diào)用撒遣,方法中無法使用this邮偎,所以它鎖的不是this,而是類的Class對象义黎,所以禾进,static synchronized方法也相當(dāng)于全局鎖,相當(dāng)于鎖住了代碼段廉涕。