Другими словами, я не хочу, чтобы поток ожидал, если он не может получить доступ к блокировке (как при синхронизации), я хочу, чтобы выполнение потока просто немедленно возвращалось в тот момент, если он не может получить блокировку.
Так как простая логическая блокировка, подобная этой, потенциально может обеспечить доступ к нескольким потокам.
private static boolean lockAvailable = true;
private boolean acquireLock() {
if(lockAvailable) {
lockAvailable = false;
return true;
}
return false;
}
Я что-то упустил? Каков наилучший / самый простой способ добиться этого?
Edit:
Спасибо, что указали на Семафор (!)
Так что, глядя на это снова, этот код является пуленепробиваемым?
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();
}
}
}
Обновление:
Я понял, что мне нужна возможность повторного входа, поэтому я создал простой не блокирующий повторный вход. Размещать код для тех, кто интересуется, как вы можете это сделать. Любой, кто хочет этот тип функциональности, должен, конечно, использовать существующий Java-класс 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();
}
}