I see in many tutorials for Reentrant lock, they create a new Reentrant lock and resource is injected, lock and unlock of reentrant lock is called in try/finally block. I don't understand the connection between this lock and resource which is used in the thread. Below is an example of tutorial on Reentrant lock
Resource code
public class Resource {
public void doSomething(){
//do some operation, DB read, write etc
}
public void doLogging(){
//logging, no need for thread safety
}
}
Resource being used in thread declaration code
public class ConcurrencyLockExample implements Runnable{
private Resource resource;
private Lock lock;
public ConcurrencyLockExample(Resource r){
this.resource = r;
this.lock = new ReentrantLock();
}
@Override
public void run() {
try {
if(lock.tryLock(10, TimeUnit.SECONDS)){
resource.doSomething();
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
//release lock
lock.unlock();
}
resource.doLogging();
}
}
Can someone explain me how this prevents multiple threads from accessing the given resource at same time resulting in race condition??? Is this reentrant lock creating a Object level lock or Class level lock in the resource???
CodePudding user response:
In one sense, a ReentrantLock
is neither a class-level nor an object-level lock. In a more practical sense, it's the same as either of them.
Really, you should forget about "class level" and "object level" locking. Those are not useful distinctions.
You can use any object as the lock
in a synchronized(lock){...}
block. "Object level" locking simply means that, within some method of a class C
, the object that you choose is an instance of the class; and "class level" means that you choose to use C.class
as the lock object.
But, you can use other objects too,* and synchronized(lock)
behaves in the same way no matter what object you choose for the lock
.
* This is a pattern that often is used by experienced software developers:
public class C {
private final Object lock = new Object();
public MyType myMethod(...) {
...
synchronized(lock) {
...
}
...
}
}
The reason for using a private
object is that the author of some client code that uses class C
potentially could use an instance of C
as a lock. That wouldn't be a smart idea, but nothing prevents them from doing it. If two different authors chose to use the same object as a lock, there could be unforseen interactions (e.g., in the worst case, a deadlock.) But the author of class C
averts that problem by using a lock
object that is inaccessible to other classes.