是同步锁定一个Reentrantlock,还是只锁定它的对象?

问题描述 投票:4回答:2

ReentrantLock和lock()/ unlock()的正常模式是这样的:

lck.lock();
try {
    // ...
}
finally {
    lck.unlock();
}

这可以重构吗?

synchronized(lck) {
    // ...
}

? 为什么?

java java.util.concurrent
2个回答
6
投票

这些是不同的东西。 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将发出警告。


1
投票

我假设你知道Locksynchronized分别提供的显式和隐式锁定的差异。

我相信你正在寻找一个理由说明在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){
    ...
    }
}

但话说回来,我没有看到任何理由为什么你不会使用多个锁实例来实现上述目的。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.