標籤:
位置: 建議127: Lock與synchronized是不一樣的
首先在概念上糾正這一篇內容:
援引Java源碼中關於ReentrantLock的開篇說明:
* A reentrant mutual exclusion {@link Lock} with the same basic
* behavior and semantics as the implicit monitor lock accessed using
* {@code synchronized} methods and statements, but with extended
* capabilities.
根據說明: 兩個加鎖方式是具有相同的基礎行為和語義的,僅僅是表現形式上和功能擴充性方面的差別,所以該建議理論是錯誤的.
以下程式碼片段的執行差異和原作者的解釋錯誤主要出現在以下幾個方面:
1. ReentrantLock和synchronized 都是對象級所,而沒有一個是類級的,因此都只能作用到代碼所影響的具體對象上去.
如 synchronized public void read(){
// some executing code region
} 其實是隱式鎖定了this;
等價於:
Lock lock = new ReentrantLock();
public void read(){
lock.lock();
try{
// some executing code region
}finally{
lock.unlock();
}
}
兩者的區別是一個monitorthis, 一個monitor lock對象.
比較特殊的情況是:
synchronized publi static execute(){
}
該類對象鎖定的是 .class對象.
下文中的不一致性主要出現在對"A"的synchronized鎖定上,
常量字串對象在整個生命週期內是全域唯一的,因此,對"A"的所是全域生效的,不僅僅在次類內部,及時全域任何對synchronized("A")都會產生同步效果,這裡違反了封閉原則,因此具有巨大的編程風險.
援引代碼錯誤:
該篇引用了兩段代碼來說明兩種方式的行為不一致性.
在這裡簡單地列舉並指出問題所在:
class1 : lock
/*****************************************************************************/
class Task {
public void doSomething(){
try{
Thread.sleep(2000);
}catch(Exception e){
// 異常處理
}
StringBuilder sb = new StringBuilder();
// 線程名稱:
sb.append(" 線程名稱: " + Thread.currentThread().getName());
// 已耗用時間戳
sb.append(",執行時間: " + Calendar.getInstance().get(13) + " s");
System.out.println(sb.toString());
}
}
/****************************************************************************/
class TaskWithLock extends Task implements Runnable{
private final Lock lock = new ReentrantLock();
@Override
public void run(){
try{
// 開始鎖定
lock.lock();
doSomething();
}finally{
// 釋放鎖
lock.unlock();
}
}
}
/***************************************************************************/
class TaskWithSync extends Task implements Runnable{
@Override
public void run(){
// 內部索
synchronized("A"){
doSomething();
}
}
}
public static void runTasks(Class<? extends Runnable> clazz) throws Exception {
try{
ExecutorService es = Executors.newCachedThreadPool();
System.out.println("***開始執行" + clazz.getSimpleName() + " 任務已執行完畢-----------------\n");
// 啟動三個線程
for ( int i=0; i<3 ; i++){
es.submit(clazz.newInstance());
}
TimeUnit.SECONDS.sleep(10);
System.out.println("--------" + clazz.getSimpleName() + " 任務執行完畢------\n");
// 關閉執行器
}finally{
es.shutdown();
}
}
秦曉波著的編寫高品質代碼-改善Java程式的151個建議一書中的線程解釋錯誤.