最近遇到一個crash問題排惨,是關(guān)于線程同步鎖的纵搁,檢查代碼的時候發(fā)現(xiàn)方法已經(jīng)使用synchronized同步了,為什么還會出現(xiàn)該異常呢?
就想是不是synchronized使用的不對呢亭珍?查了些資料發(fā)現(xiàn)好像synchronized真用錯了铛纬!
我們先看一下synchronized的官方定義:
synchronized Java語言的關(guān)鍵字章喉,可用來給對象和方法或者代碼塊加鎖梅肤,當它鎖定一個方法或者一個代碼塊的時候,同一時刻最多只有一個線程執(zhí)行這段代碼沐序。
我們先來看一段代碼
public class Test {
class DB {
private synchronized void query() {
System.out.println("開始查詢");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("查詢結(jié)束");
}
}
private static Runnable myRunnable = new Runnable() {
@Override
public void run() {
DB db = new Test().new DB();
db.query();
}
};
public static void main(String[] args) throws Exception {
Thread thread1 = new Thread(myRunnable);
thread1.start();
Thread thread2 = new Thread(myRunnable);
thread2.start();
}
}
我們看下輸出結(jié)果
開始查詢
開始查詢
查詢結(jié)束
查詢結(jié)束
咦 我的方法已經(jīng)加了synchronized了琉用,為什么還是用兩個線程同時執(zhí)行了query方法呢?是不是synchronized加錯地方了呢?
我們改下代碼 把query方法前面的synchronized 去掉 放到方法里面去
private void query() {
synchronized (this) {
System.out.println("開始查詢");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("查詢結(jié)束");
}
}
這樣我鎖住這個代碼塊應(yīng)該就沒問題了吧策幼!我們來看下運行結(jié)果
開始查詢
開始查詢
查詢結(jié)束
查詢結(jié)束
恩邑时?怎么還是兩個線程同時執(zhí)行了query方法呢?
想一個問題 synchronized 到底鎖住的是方法,是代碼塊還是對象特姐?
來看下面代碼
private void query() {
synchronized (DB.class) {
System.out.println("開始查詢");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("查詢結(jié)束");
}
}
}
把上面的的synchronized (this)改成 synchronized (DB.class) 晶丘,執(zhí)行程序看下結(jié)果
開始查詢
查詢結(jié)束
開始查詢
查詢結(jié)束
終于輸出了我們期望的結(jié)果。
說下上面的問題唐含,synchronized 鎖住的是什么浅浮?
synchronized 鎖住的是括號里面的對象!
我們回頭看下第一段代碼把synchronized 加到方法前面捷枯,怎么能輸出正確的結(jié)果滚秩?我們改下代碼
public class Test {
class DB {
private synchronized void query() {
System.out.println("開始查詢");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("查詢結(jié)束");
}
}
private static DB db = new Test().new DB();
private static Runnable myRunnable = new Runnable() {
public void run() {
db.query();
}
};
public static void main(String[] args) throws Exception {
Thread thread1 = new Thread(myRunnable);
thread1.start();
Thread thread2 = new Thread(myRunnable);
thread2.start();
}
}
執(zhí)行程序 看下輸出結(jié)果
開始查詢
查詢結(jié)束
開始查詢
查詢結(jié)束
程序正常輸出
當synchronized加到方法前面時,當有多個線程同時訪問 ”同一個對象” 的synchronized方法時淮捆,一個時間內(nèi)只能有一個線程得到執(zhí)行叔遂。另一個線程必須等待當前線程執(zhí)行完這個方法才能執(zhí)行。
synchronized有風(fēng)險争剿,要用需謹慎啊痊末!