ReentrantLock和lock()/ unlock()的正常模式是这样的:
lck.lock();
try {
// ...
}
finally {
lck.unlock();
}
这可以重构吗?
synchronized(lck) {
// ...
}
? 为什么?
这些是不同的东西。 synchronized
内置于该语言中,可与任何对象一起使用。它的作用是锁定其内在锁定。每个对象都有一个。由于它是一个内置机制,你不需要一个try-finally
块 - 当控件退出synchronized
块时,锁总是被解锁。因此,只要您的代码实际退出该块,锁就会被解锁。
ReentrantLock
是一个特殊的班级。它锁定了一些特殊的内部对象,这可能是特定于实现的。它不会锁定其内在锁定。当然,你也可以锁定那个 - 但它通常没有任何意义。这段代码几乎肯定会死锁,例如:
final ReentrantLock lock = new ReentrantLock();
new Thread(() -> {
lock.lock();
try {
System.out.println("Thread 1 locked the lock");
try { Thread.sleep(100); } catch (Exception ex) {}
synchronized (lock) {
System.out.println("Thread 1 locked lock's intrinsic lock");
}
} finally {
lock.unlock();
}
}).start();
new Thread(() -> {
synchronized (lock) {
System.out.println("Thread 2 locked lock's intrinsic lock");
try { Thread.sleep(200); } catch (Exception ex) {}
lock.lock();
try {
System.out.println("Thread 2 locked the lock");
} finally {
lock.unlock();
}
}
}).start();
它死锁,因为两个线程以不同的顺序锁定两个不同的东西。
肯定感觉ReentrantLock
几乎和synchronized
一样。它的工作方式类似,但synchronized
既方便又不强大。因此,除非你需要ReentrantLock
的任何功能,比如可中断的锁定尝试或锁定超时,你应该坚持使用synchronized
进行重入锁定,并使用任何对象。简单的private final Object lock = new Object()
会做得很好。请注意,如果您在某个时刻更改该对象,final
将防止可能发生的混乱;如果省略final
,某些IDE将发出警告。
我假设你知道Lock
和synchronized
分别提供的显式和隐式锁定的差异。
我相信你正在寻找一个理由说明在Lock
中使用synchronized
块中实现synchronized(lock)
接口的类的实例有什么问题。
可以重构吗?是。
但是你应该这样做吗?不是实现Lock接口的类的实例
为什么? - 好。
如果你只是在lock
里面使用synchronized
就可以了,但是你可以让其他开发人员误用代码,例如:如果有人明天尝试在Lock
中调用synchronized(lock)
方法,如下所示。
Lock lock = new ReentrantLock();
synchronized(lock){ //You write this
// lock.lock(); // I am not taking a lock here
System.out.println("See emily play");
...
...
... // after 100 lines of code
//callAnotherMethod(lock); //Someone else does this
lock.unlock(); //Someone else does this
}
上面的代码很糟糕,但举个例子,在上面的例子中,如果你不调用lock()
,那么你最终会得到IllegalMonitorStateException
。如果你打电话(在上面取消注释)lock.lock()
它没有任何区别。
更不用说你传递锁定实例的callAnotherMethod(lock)
以及它可以引入的那种意外行为。
请记住,这是一个这样的例子。
最重要的是,如果任何机会都能正常运作,那就是浪费资源和时间,没有任何优势/目的。更重要的是,不能保证它不会在未来引入回归。如果会出现任何此类回归,最终可能会因为滥用概念而浪费大量时间。
软件总是采用Open-Close原则设计。您将编写非常清楚地违反它的代码。
如果您确实想使用synchronized
使用细粒度锁,那么您可以使用下面的内容
Object obj1 = new Object();
Object obj2 = new Object();
public void doSomething(){
synchronised(obj1){
...
}
}
public void doSomethingMore(){
synchronised(obj2){
...
}
}
但话说回来,我没有看到任何理由为什么你不会使用多个锁实例来实现上述目的。