如何确定对象是否被锁定(同步)以免在 Java 中阻塞?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1779795/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do determine if an object is locked (synchronized) so not to block in Java?
提问by Shaitan00
I have a process A that contains a table in memory with a set of records (recordA, recordB, etc...)
我有一个进程 A,它在内存中包含一个表,其中包含一组记录(recordA、recordB 等...)
Now, this process can launch many threads that affect the records, and sometimes we can have 2 threads trying to access the same record - this situation must be denied. Specifically if a record is LOCKED by one thread I want the other thread to abort (I do not want to BLOCK or WAIT).
现在,这个进程可以启动许多影响记录的线程,有时我们可以有 2 个线程试图访问同一个记录 - 这种情况必须被拒绝。特别是如果记录被一个线程锁定,我希望另一个线程中止(我不想阻塞或等待)。
Currently I do something like this:
目前我做这样的事情:
synchronized(record)
{
performOperation(record);
}
But this is causing me problems ... because while Process1 is performing the operation, if Process2 comes in it blocks/waits on the synchronized statement and when Process1 is finished it performs the operation. Instead I want something like this:
但这给我带来了问题......因为在 Process1 执行操作时,如果 Process2 进入它会阻塞/等待同步语句,当 Process1 完成时它会执行操作。相反,我想要这样的东西:
if (record is locked)
return;
synchronized(record)
{
performOperation(record);
}
Any clues on how this can be accomplished? Any help would be much appreciated. Thanks,
关于如何实现这一点的任何线索?任何帮助将非常感激。谢谢,
采纳答案by Jon Skeet
One thing to note is that the instantyou receive such information, it's stale. In other words, you could be told that no-one has the lock, but then when you try to acquire it, you block because another thread took out the lock between the check and you trying to acquire it.
有一点要注意的是,即时你收到这样的信息,它已经过期。换句话说,您可能会被告知没有人拥有该锁,但是当您尝试获取它时,您会阻塞,因为另一个线程在检查和您尝试获取它之间取出了锁。
Brian is right to point at Lock
, but I think what you really want is its tryLock
method:
Brian 指出 是正确的Lock
,但我认为您真正想要的是它的tryLock
方法:
Lock lock = new ReentrantLock();
......
if (lock.tryLock())
{
// Got the lock
try
{
// Process record
}
finally
{
// Make sure to unlock so that we don't cause a deadlock
lock.unlock();
}
}
else
{
// Someone else had the lock, abort
}
You can also call tryLock
with an amount of time to wait - so you could try to acquire it for a tenth of a second, then abort if you can't get it (for example).
您也可以tryLock
在等待一段时间后调用- 因此您可以尝试在十分之一秒内获取它,然后在无法获取时中止(例如)。
(I think it's a pity that the Java API doesn't - as far as I'm aware - provide the same functionality for the "built-in" locking, as the Monitor
class does in .NET. Then again, there are plenty of other things I dislike in both platforms when it comes to threading - every object potentially having a monitor, for example!)
(我认为很遗憾 Java API 没有 - 据我所知 - 为“内置”锁定提供相同的功能,就像Monitor
类在 .NET 中所做的那样。再说一次,有很多当涉及到线程时,我在两个平台上都不喜欢的其他东西 - 例如,每个对象都可能有一个监视器!)
回答by Brian Agnew
Take a look at the Lockobjects introduced in the Java 5 concurrency packages.
查看Java 5 并发包中引入的Lock对象。
e.g.
例如
Lock lock = new ReentrantLock()
if (lock.tryLock()) {
try {
// do stuff using the lock...
}
finally {
lock.unlock();
}
}
...
The ReentrantLockobject is essentially doing the same thing as the traditional synchronized
mechanism, but with more functionality.
该ReentrantLock的对象基本上是在做同样的事情,传统synchronized
机制,但有更多的功能。
EDIT: As Jon has noted, the isLocked()
method tells you at that instant, and thereafter that information is out of date. The tryLock()method will give more reliable operation (note you can use this with a timeout as well)
编辑:正如 Jon 所指出的,该isLocked()
方法会在那一刻告诉您,此后该信息已过时。该的tryLock()方法会给更可靠的操作(注意,你可以用一个超时使用为好)
EDIT #2: Example now includes tryLock()/unlock()
for clarity.
编辑#2:tryLock()/unlock()
为了清楚起见,现在包括示例。
回答by PSpeed
While the Lock answers are very good, I thought I'd post an alternative using a different data structure. Essentially, your various threads want to know which records are locked and which aren't. One way to do this is to keep track of the locked records and make sure that data structure has the right atomic operations for adding records to the locked set.
虽然 Lock 的答案非常好,但我想我会使用不同的数据结构发布一个替代方案。本质上,您的各个线程想知道哪些记录被锁定,哪些没有。一种方法是跟踪锁定的记录并确保数据结构具有将记录添加到锁定集的正确原子操作。
I will use CopyOnWriteArrayList as an example because it's less "magic" for illustration. CopyOnWriteArraySet is a more appropriate structure. If you have lots and lots of records locked at the same time on average then there may be performance implications with these implementations. A properly synchronized HashSet would work too and locks are brief.
我将使用 CopyOnWriteArrayList 作为示例,因为它不那么“神奇”来说明。CopyOnWriteArraySet 是更合适的结构。如果您平均有大量记录同时锁定,那么这些实现可能会对性能产生影响。正确同步的 HashSet 也可以工作,而且锁很简短。
Basically, usage code would look like this:
基本上,使用代码如下所示:
CopyOnWriteArrayList<Record> lockedRecords = ....
...
if (!lockedRecords.addIfAbsent(record))
return; // didn't get the lock, record is already locked
try {
// Do the record stuff
}
finally {
lockedRecords.remove(record);
}
It keeps you from having to manage a lock per record and provides a single place should clearing all locks be necessary for some reason. On the other hand, if you ever have more than a handful of records then a real HashSet with synchronization may do better since the add/remove look-ups will be O(1) instead of linear.
它使您不必为每个记录管理一个锁,并提供一个单一的地方,如果出于某种原因需要清除所有锁。另一方面,如果您有多个记录,那么带有同步的真正 HashSet 可能会做得更好,因为添加/删除查找将是 O(1) 而不是线性的。
Just a different way of looking at things. Just depends on what your actual threading requirements are. Personally, I would use a Collections.synchronizedSet( new HashSet() ) because it will be really fast... the only implication is that threads may yield when they otherwise wouldn't have.
只是看待事物的方式不同。只取决于您的实际线程要求是什么。就我个人而言,我会使用 Collections.synchronizedSet( new HashSet() ) 因为它会非常快......唯一的含义是线程可能会产生,否则它们可能不会产生。
回答by Chris Wraith
Whilst the above approach using a Lock object is the best way to do it, if you have to be able to check for locking using a monitor, it can be done. However, it does come with a health warning as the technique isn't portable to non Oracle Java VMs and it may break in future VM versions as it isn't a supported public API.
虽然使用 Lock 对象的上述方法是最好的方法,但如果您必须能够使用监视器检查锁定,则可以完成。但是,它确实带有健康警告,因为该技术不能移植到非 Oracle Java VM,并且它可能会在未来的 VM 版本中失效,因为它不是受支持的公共 API。
Here is how to do it:
这是如何做到的:
private static sun.misc.Unsafe getUnsafe() {
try {
Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (Unsafe) field.get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void doSomething() {
Object record = new Object();
sun.misc.Unsafe unsafe = getUnsafe();
if (unsafe.tryMonitorEnter(record)) {
try {
// record is locked - perform operations on it
} finally {
unsafe.monitorExit(record);
}
} else {
// could not lock record
}
}
My advice would be to use this approach only if you cannot refactor your code to use java.util.concurrent Lock objects for this and if you are running on an Oracle VM.
我的建议是,仅当您无法重构代码以为此使用 java.util.concurrent Lock 对象并且您在 Oracle VM 上运行时才使用这种方法。
回答by Rahul Kulshreshtha
I found this, we can use Thread.holdsLock(Object obj)
to check if an object is locked:
我发现了这个,我们可以Thread.holdsLock(Object obj)
用来检查一个对象是否被锁定:
Returns
true
if and only if the current thread holds the monitor lock on the specified object.
true
当且仅当当前线程持有指定对象的监视器锁时才返回。
Note that Thread.holdsLock()
returns false
if the lock is held by somethingand the calling thread isn't the thread that holds the lock.
需要注意的是Thread.holdsLock()
回报率false
,如果锁被持有的东西,并调用线程是不是持有锁的线程。
回答by HRgiger
Another workaround is (in case of you didnt have chance with the answers given here )is using timeouts. i.e. below one will return null after 1 second hanging:
另一个解决方法是(如果您没有机会获得此处给出的答案)正在使用超时。即低于一个将在挂起 1 秒后返回 null:
ExecutorService executor = Executors.newSingleThreadExecutor();
//create a callable for the thread
Future<String> futureTask = executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return myObject.getSomething();
}
});
try {
return futureTask.get(1000, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
//object is already locked check exception type
return null;
}
回答by The Schwartz
Thanks for this, it helped me out solving a race condition. I changed it a little to wear both belt and suspenders.
感谢这一点,它帮助我解决了竞争条件。我稍微改变了一下,既穿腰带又穿吊带。
So here is my suggestion for AN IMPROVEMENT of the accepted answer:
所以这是我对已接受答案的改进的建议:
You can ensure that you get safe access to the tryLock()
method by doing something like this:
您可以tryLock()
通过执行以下操作来确保安全访问该方法:
Lock localLock = new ReentrantLock();
private void threadSafeCall() {
boolean isUnlocked = false;
synchronized(localLock) {
isUnlocked = localLock.tryLock();
}
if (isUnlocked) {
try {
rawCall();
}
finally {
localLock.unlock();
}
} else {
LOGGER.log(Level.INFO, "THANKS! - SAVED FROM DOUBLE CALL!");
}
}
This would avoid the situation where you might get two calling tryLock()
at the almost same time, causing the return to be potentially doubt full. I'd like to now if I'm wrong, I might be over cautios here. But hey! My gig is stable now :-)..
这将避免您可能tryLock()
几乎同时接到两个电话的情况,从而导致返回可能被怀疑已满。如果我错了,我现在想,我在这里可能会过度谨慎。但是嘿!我的演出现在很稳定:-)..
Read more on my development issues at my Blog.
在我的博客上阅读更多关于我的开发问题的信息。