秦晓波著的编写高质量代码-改善Java程序的151个建议一书中的线程解释错误.
位置: 建议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();
}
}