In others words I don't want a thread to wait if it cannot access the lock (as in synchonization), I want to thread execution to simply return immediately at that point if it cannot obtain the lock.
As simple boolean lock like this would potentially allow more than one thread to access.
private static boolean lockAvailable = true;
private boolean acquireLock() {
if(lockAvailable) {
lockAvailable = false;
return true;
}
return false;
}
Am I missing something? What is the best/simplest way to achieve this?
Edit:
Thanks for pointing out Semaphore (!)
So looking at it again this code is bullet proof?
private final static Semaphore lock = new Semaphore(1, true);
public void tryAndDoSomething() {
if(lock.tryAcquire()) {
try {
// only single thread can access here at one time
} finally {
lock.release();
}
}
}
Update:
I realised I needed reentrant capability so I created a simple non blocking reentrant. Posting the code for anyone that is interested in how you might do this. Anyone who开发者_如何学JAVA wants this type of functionality should of course use the existing Java Class java.util.concurrent.locks.ReentrantLock :|
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* SimpleNonBlockingLock ensures that only a single thread can call protected code at any one time,
* while allowing other threads to by pass the protected code if the lock is unavailable.
* The thread owning the lock can access any code protected by the lock (the lock is 'reentrant').
* To function correctly the protected code must be executed in a try/finally blocks. The
* finally block must call the tryRelease. Example code:
*
* private final SimpleNonBlockingLock lock = new SimpleNonBlockingLock();
*
* if(lock.tryAcquire()) {
* try {
* // access protected code
* } finally {
* lock.tryRelease();
* }
* }
*
* This code is for demonstration only and should not be used. I have tested it and it 'seems to' work.
* However it may contain horrific bugs!
*
* The Java class java.util.concurrent.locks.ReentrantLock has been around since Java 5.0 and contains all (and more)
* of this functionality. Its also been thoroughly tested!
*/
public class SimpleNonBlockingLock {
// Atomic locking mechanism
private final AtomicBoolean locked = new AtomicBoolean();
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Unique ID of thread which currently has lock
private int threadUniqueId = -1;
// Tracks number of tryAcquire calls made by thread with lock
private int lockCount = 0;
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
@Override protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
public synchronized boolean tryAcquire() {
// Allow owning thread to acquire
if(threadUniqueId == getCurrentThreadUniqueId()) {
lockCount++;
return true;
}
// If locked then do not allow
if (locked.get()) {return false;}
// Attempt to acquire lock
boolean attemptAcquire = locked.compareAndSet(false, true);
// If successful then set threadUniqueId for the thread, and increment lock count
if(attemptAcquire) {
threadUniqueId = getCurrentThreadUniqueId();
lockCount++;
}
// Return result of attempt to acquire lock
return attemptAcquire;
}
public synchronized boolean tryRelease() {
if (!locked.get()) {
// Lock is currently available - no need to try and release
return true;
} else {
// Decrement the lock count
lockCount--;
// If lock count is zero we release lock, and reset the threadUniqueId
if(lockCount == 0) {
threadUniqueId = -1;
return locked.compareAndSet(true, false);
}
return false;
}
}
// Returns the current thread's unique ID, assigning it if necessary
public static int getCurrentThreadUniqueId() {
return threadId.get();
}
}
Java 5 has introduced explicit locks which have a tryLock
operation. So use an explicit lock instead of synchronized blocks, then you can call tryLock
:
private Lock lock = ...;
private boolean acquireLock() {
if (lock.tryLock()) {
...
return true;
} else {
return false;
}
}
Use a Semaphore
and the tryAcquire
method.
You should try using the ReentrantLock class. You can try to acquire by using tryLock() or call lock() directly. Make sure you study the API for ReentrantLock. This is a brief example:
Lock lock = new ReentrantLock();
// block until lock is acquired. Make sure to call unlock in a finally
// statement!
try {
lock.lock();
} finally {
lock.unlock();
}
// or try to gain the lock
boolean success = lock.tryLock();
if(success) {
//some logic..
}
//or try to gain lock within time frame
try {
lock.tryLock(1, TimeUnit.SECONDS);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
Others have pointed out that there is excellent library to do all this and more, but if you were interested in implementing this sort of thing then you need something that supports a compare-and-set operation. Fortunately, from Java5 there is the java.util.concurrent.atomic.Atomic* classes!
public class MyLock {
private final AtomicBoolean locked = new AtomicBoolean();
public boolean tryLock() {
if (locked.get()) return true;
return locked.compareAndSet(false, true);
}
public boolean unlock() {
if (l!ocked.get()) return true;
return locked.compareAndSet(true, false);
}
}
The above is a very naïve implementation, but it shows the basics of how you'd implement this is a lock-free manner.
For more information on the implementation details of concurrent data-structures see Shavit and Herlihy's awesome "The Art of Multiprocessor Programming" and of course the essential "Java Concurrency in Practice" by Goetz et al.
yes you are missing something (your solution won't work)
imagine 2 threads get to the if() statement at the same time, and pass it. both set lockAvailable to false and return true
you have to make the function synchronized if you don't want to violate critical section rules, or use a different method.
精彩评论