Is it possible to aquire a lock in one method and release it from a different method?
I basically I am using a frame work that calls a bunch of functions from two different threads. I开发者_开发技巧 would like one entire thread to complete before the next thread is allow to continue. However, I can not change there code.
For example suppose there are two threads, thread 1 and thread 2. Each thread calls methods one through three.
So you get a call order something like:
T1-M1 T1-M2 T2-M1 T1-M3 T2-M2 T2-M3
However, what I would like to have happen is T1-M1 one sets some sort of lock that will block T2, so the order will be as follows.
T1-M1 - get lock T2-M1 - blocked T1-M2 T1-M3 - release lock T2-M1 - no longer blocked - gets lock T2-M2 T2-M3
Is it possible to do this in Java without editing the calling method from the framework?
You can't do that with the simple features provided by the synchronized
keyword: its lock must be released in the same method (even block) on which it's acquired.
You can use the tools in java.util.concurrent.locks
, however.
It's ReentrantLock
class is pretty much a reification of the synchronized
mechanism.
Bear in mind that such an approach is dangerous, however. What if a thread calls M1
and M2
, but never calls M3
? It might get an exception in some code between the calls to M2
and M3
, for example.
You cannot release an intrinsic lock from another method but you can release a j.u.c.Lock from another method. For example: (Edited based on Peter Lawrey's response, thanks)
public boolean tryAcquireTimedLock(){
return lock.tryLock(30, TimeUnit.SECONDS);
}
public void releaseLock(){
lock.unlock();
}
Here offers a convenience method to acquire a lock interruptbly with a timeout.
Now lets get to the pitfalls. If you lock and unlock from different method you have to be careful of any exception that can occur during the process. Failure to account for this can lead to a deadlock issue.
public void doSomeWork(){
if(tryAcquireTimedLock()){
otherMethodWork();
finalMethodWork();
}
}
public void otherMethodWork(){
//do something that may cause an Exception
}
public void finalMethodWork(){
//finish work now release lock
releaseLock();
}
Now what if otherMethodWork()
throws a RuntimeException
? Your code will now never be able to invoke releaseLock()
and no more progress can be made.
This is why it is instructed to use the style
lock.lock();
try{
//work
}finally{
lock.unlock();
}
If anything wrong happens in //work you will still be able to unlock and make progress.
If you do not want to control the calling from the framework layer you can do it by setting this logic into every thread you have validating when they can access to every single method, you can use session variables as flags for example to control when the method is available to any other so you can just call the threads and they will have the validations to access the methods.-
Just a complement of the other posts, all 3 methods have to share the same lock, the shared resource here is the bundle of the 3 methods, you don't want T1 getting a lock on M1 and M2 when T2 has already a lock on M3.
精彩评论