简单的 Java 代码允许单线程访问和其他线程跳过/继续
换句话说,我不希望线程在无法访问锁时等待(如在同步中),我希望线程执行在无法获取锁时立即返回。
像这样的简单布尔锁可能会允许多个线程访问。
private static boolean lockAvailable = true;
private boolean acquireLock() {
if(lockAvailable) {
lockAvailable = false;
return true;
}
return false;
}
我错过了什么吗?实现这一目标的最佳/最简单方法是什么?
编辑:
感谢您指出信号量(!)
所以再看一遍这段代码是防弹的吗?
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();
}
}
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 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();
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
Java 5 引入了显式锁 其中有一个
tryLock
操作。所以使用显式锁而不是同步块,然后你可以调用tryLock
:Java 5 has introduced explicit locks which have a
tryLock
operation. So use an explicit lock instead of synchronized blocks, then you can calltryLock
:使用
信号量
< /a> 和tryAcquire
方法。Use a
Semaphore
and thetryAcquire
method.您应该尝试使用 ReentrantLock 类。
您可以尝试使用tryLock()或直接调用lock()来获取。确保您研究了 ReentrantLock 的 API。
这是一个简短的例子:
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:
其他人指出,有一个优秀的库可以完成所有这些以及更多操作,但是如果您有兴趣实现此类事情,那么您需要一些支持比较和设置操作的东西。幸运的是,从 Java5 开始,出现了 java.util.concurrent.atomic.Atomic* 类!
上面是一个非常简单的实现,但它展示了如何以无锁方式实现这一点的基础知识。
有关并发数据结构的实现细节的更多信息,请参阅 Shavit 和 Herlihy 的精彩“多处理器编程的艺术”,当然还有 Goetz 等人撰写的重要的“Java 并发实践”。
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!
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.
是的,你错过了一些东西(你的解决方案将不起作用)
想象2个线程同时到达 if() 语句,并传递它。
两者都将 lockAvailable 设置为 false 并返回 true,
如果您不想违反关键部分规则,则必须使函数同步,或者使用不同的方法。
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.