在java多線程并發(fā)編程中對于 sychronized 大家一定不陌生,同步關鍵字 synchronized 是java語言中最為常用的同福方法之一伴鳖。那什么情況下才用到 sychronized 呢鸿市?怎么用呢
當有多個線程同時對一個變量進行讀寫的時候就可能出現(xiàn)數(shù)據(jù)不一致的現(xiàn)象锯梁,這時候就需要同步,限制為排隊一個個來焰情。就好比你去買水果陌凳,稱水果的時候如果與別人共用以個袋子,你自己到底買了多少水果你根本不會知道内舟。這時候就是 sychronized 上場的時候了合敦。簡單說 sychronized 就是為了保證數(shù)據(jù)安全的。
sychronized 一般用來修飾一個方法或者一個代碼塊验游。利用sychronized實現(xiàn)同步的基礎:java中每一個對象都可以作為鎖充岛。具體表現(xiàn)為以下3中形式。
1.對于普通方法的同步耕蝉,鎖是當前實例對象崔梗。
2.對于靜態(tài)方法的同步,鎖是當前類的Class對象赔硫。
3.對于同步方法塊炒俱,鎖是sychronized括號里配置的對象。
下面用代碼來演示一遍爪膊。
一权悟、用sychronized修飾一個方法的時候
1.sychronized修飾普通方法
public class ThreadTest extends Thread{
private Test test;
public ThreadTest(String name,Test test) {
super(name);
this.test = test;
}
@Override
public void run() {
test.method();
}
}
public class Test{
public static void main(String[] args) {
Test test1 = new Test();
Test test2 = new Test();
ThreadTest thread1 = new ThreadTest("線程1", test1);
ThreadTest thread2 = new ThreadTest("線程2", test1); //(1)
// ThreadTest thread2 = new ThreadTest("線程2", test2); //(2)
thread1.start();
thread2.start();
}
public synchronized void method(){
System.out.println(Thread.currentThread().getName()+" 獲得鎖");
try {
System.out.println("開始方法......");
Thread.sleep(2000); //模擬做其他事情
System.out.println("結束方法......");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
上面程序執(zhí)行的結果是:
線程1 獲得鎖
開始方法......
結束方法......
線程2 獲得鎖
開始方法......
結束方法......
當把代碼(1)注釋掉,打開代碼(2)推盛,執(zhí)行結果是:
線程1 獲得鎖
線程2 獲得鎖
開始方法......
開始方法......
結束方法......
結束方法......
結果分析:
從結果(1)可以看出峦阁,是同一實例對象的時候,多線程同一時刻只能有一個線程能獲得鎖耘成,其他線程必須等鎖釋放了才能獲取榔昔。
從結果(2)可以看出,當不是同一個實例對象的時候瘪菌,線程獲得的鎖與其他線程無關撒会,不需等待其他線程的鎖釋放。
綜合得出:sychronized修飾普通方法的時候师妙,鎖是當前實例對象诵肛,每個實例對象一把鎖,同一時刻只能一個線程獲得鎖默穴。不同實例對象獲取的鎖不一樣怔檩,不需等待其他實例對象鎖的釋放褪秀。
2.sychronized修飾靜態(tài)方法
public synchronized static void method(){
System.out.println(Thread.currentThread().getName()+" 獲得鎖");
try {
System.out.println("開始方法......");
Thread.sleep(2000); //模擬做其他事情
System.out.println("結束方法......");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
當把method()改為靜態(tài)的時候。按照上面的執(zhí)行方法薛训,執(zhí)行結果都是
線程1 獲得鎖
開始方法......
結束方法......
線程2 獲得鎖
開始方法......
結束方法......
從結果可以得出:sychronized修飾靜態(tài)方法的時候媒吗,鎖是當前類的Class對象,每個類對應一把鎖乙埃,多線程不同實例對象同一時刻也只能是一個獲得鎖闸英,其他必須等待鎖的釋放。
二膊爪、用sychronized修飾代碼塊的時候
1.sychronized(object)括號配置的是普通變量的時候
public class Test{
private Object object = new Object();
public static void main(String[] args) {
Test test1 = new Test();
Test test2 = new Test();
ThreadTest thread1 = new ThreadTest("線程1", test1);
ThreadTest thread2 = new ThreadTest("線程2", test1); //(1)
// ThreadTest thread2 = new ThreadTest("線程2", test2); //(2)
thread1.start();
thread2.start();
}
public void method(){
synchronized (object) {
System.out.println(Thread.currentThread().getName()+" 獲得鎖");
try {
System.out.println("開始方法......");
Thread.sleep(2000); //模擬做其他事情
System.out.println("結束方法......");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
上面程序執(zhí)行的結果是:
線程1 獲得鎖
開始方法......
結束方法......
線程2 獲得鎖
開始方法......
結束方法......
當把代碼(1)注釋掉自阱,打開代碼(2)嚎莉,執(zhí)行結果是:
線程1 獲得鎖
線程2 獲得鎖
開始方法......
開始方法......
結束方法......
結束方法......
結果分析:
從結果(1)可以看出米酬,是同一實例對象的時候(變量object一樣),多線程同一時刻只能有一個線程能獲得鎖趋箩,其他線程必須等鎖釋放了才能獲取赃额。
從結果(2)可以看出,當不是同一個實例對象的時候(變量object不一樣)叫确,線程獲得的鎖與其他線程無關跳芳,不需等待其他線程的鎖釋放。
綜合得出:sychronized(object){} object為普通變量的時候竹勉,每個實例對象一把鎖飞盆,同一時刻只能一個線程獲得鎖。不同實例對象獲取的鎖不一樣次乓,不需等待其他實例對象鎖的釋放吓歇。
2.sychronized(object)括號配置的是靜態(tài)變量的時候
private static Object object = new Object();
當把object變量改為靜態(tài)的時候(所有實例對象的object都一樣),執(zhí)行結果都是如下:
線程1 獲得鎖
開始方法......
結束方法......
線程2 獲得鎖
開始方法......
結束方法......
結果分析:sychronized(object){} object為靜態(tài)變量的時候票腰,鎖是當前類的Class對象城看,每個類對應一把鎖,多線程不同實例對象同一時刻也只能是一個獲得鎖杏慰,其他必須等待鎖的釋放测柠。
3.sychronized(object)括號配置的是類Class的時候
public void method(){
synchronized (Test.class) {
System.out.println(Thread.currentThread().getName()+" 獲得鎖");
try {
System.out.println("開始方法......");
Thread.sleep(2000); //模擬做其他事情
System.out.println("結束方法......");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
當sychronized(object)括號配置的是類Class的時候 執(zhí)行結果都是如下:
線程1 獲得鎖
開始方法......
結束方法......
線程2 獲得鎖
開始方法......
結束方法......
結果分析:sychronized(object){} object為類Class的時候,鎖是當前類的Class對象缘滥,每個類對應一把鎖轰胁,多線程不同實例對象同一時刻也只能是一個獲得鎖,其他必須等待鎖的釋放朝扼。
總結:
A赃阀、A. 無論synchronized關鍵字是修飾在方法上還是代碼塊,如果它作用的對象是非靜態(tài)的吟税,則它取得的鎖是對象凹耙,每個實例對象一把鎖姿现;如果synchronized作用的對象是一個靜態(tài)方法或一個類,則它取得的鎖是對類肖抱,該類所有的對象同一把鎖备典。
B. 每個對象只有一個鎖(lock)與之相關聯(lián),誰拿到這個鎖誰就可以運行它所控制的那段代碼意述。
在用synchronized修飾方法時要注意以下幾點:
- synchronized關鍵字不能繼承提佣。
雖然可以使用synchronized來定義方法,但synchronized并不屬于方法定義的一部分荤崇,因此拌屏,synchronized關鍵字不能被繼承。如果在父類中的某個方法使用了synchronized關鍵字术荤,而在子類中覆蓋了這個方法倚喂,在子類中的這個方法默認情況下并不是同步的,而必須顯式地在子類的這個方法中加上synchronized關鍵字才可以瓣戚。當然端圈,還可以在子類方法中調用父類中相應的方法,這樣雖然子類中的方法不是同步的子库,但子類調用了父類的同步方法舱权,因此,子類的方法也就相當于同步了仑嗅。這兩種方式的例子代碼如下:
在子類方法中加上synchronized關鍵字
class Parent {
public synchronized void method() { }
}
class Child extends Parent {
public synchronized void method() { }
}
在子類方法中調用父類的同步方法
class Parent {
public synchronized void method() { }
}
class Child extends Parent {
public void method() { super.method(); }
}
在定義接口方法時不能使用synchronized關鍵字宴倍。
構造方法不能使用synchronized關鍵字,但可以使用synchronized代碼塊來進行同步仓技。