如何确定一个对象是否被locking(同步),以防止在Java中阻塞?
我有一个进程A包含一个logging中的一个表(loggingA,loggingB等)
现在,这个过程可以启动许多影响logging的线程,有时我们可以有2个线程试图访问相同的logging – 这种情况必须被拒绝。 特别是如果一条logging被一个线程locking,我想另一个线程中止(我不想阻塞或等待)。
目前我做这样的事情:
synchronized(record) { performOperation(record); }
但是这会导致我的问题…因为当Process1执行操作时,如果Process2进入,它会在synchronized语句上阻塞/等待,当Process1完成时它将执行操作。 相反,我想要这样的东西:
if (record is locked) return; synchronized(record) { performOperation(record); }
任何线索如何可以完成? 任何帮助将非常感激。 谢谢,
有一件事要注意的是,你收到这样的信息的时刻已经过时了。 换句话说,你可以被告知,没有人有锁,但是当你试图获得它的时候,你阻塞了,因为另一个线程取出了支票和你试图获得它之间的锁。
布赖恩是正确的指向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 }
你也可以调用tryLock
一段时间等待 – 所以你可以尝试获取它的十分之一秒,然后中止,如果你不能得到它(例如)。
(我觉得很可惜,就我所知,Java API并没有提供与“内置”locking相同的function,就像Monitor
类在.NET中所做的那样。在线程中,我不喜欢这两个平台的其他东西 – 例如每个可能有监视器的对象!)
查看Java 5并发包中引入的Lock对象。
例如
Lock lock = new ReentrantLock() if (lock.tryLock()) { try { // do stuff using the lock... } finally { lock.unlock(); } } ...
ReentrantLock对象本质上与传统的synchronized
机制做同样的事情,但具有更多的function。
编辑:正如乔恩已经注意到, isLocked()
方法告诉你在那个瞬间 ,并且此后信息是过时的。 tryLock()方法将提供更可靠的操作(注意,您也可以在超时时使用它)
编辑#2:示例现在包括tryLock()/unlock()
为清楚起见。
虽然使用Lock对象的上述方法是最好的方法,但如果必须能够使用监视器检查locking,则可以完成。 但是,由于该技术不能移植到非Oracle Java虚拟机,因此会出现健康警告,并且可能会在未来的VM版本中崩溃,因为它不是受支持的公共API。
这里是如何做到这一点:
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 } }
我的build议是使用这种方法,只有当你不能重构你的代码才能使用java.util.concurrent Lock对象,并且你正在Oracle VM上运行。
虽然locking的答案是非常好的,我想我会发布一个替代使用不同的数据结构。 本质上,你的各种线程想要知道哪些logging被locking,哪些不是。 一种方法是跟踪locking的logging,并确保数据结构具有正确的primefaces操作,以便将logging添加到locking的集合中。
我将使用CopyOnWriteArrayList作为例子,因为它不太“神奇”的插图。 CopyOnWriteArraySet是一个更合适的结构。 如果平均同时有大量logging被locking,那么这些实现可能会对性能产生影响。 正确同步的HashSet也可以工作,并且locking很简短。
基本上,使用代码如下所示:
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); }
它使您不必pipe理每条logging的锁,并提供一个地方应清除所有锁是必要的,出于某种原因。 另一方面,如果你有不止一条logging,那么带有同步的实际HashSet可能会更好,因为添加/删除查找将是O(1)而不是线性的。
只是看待事物的一种不同的方式。 只取决于你的实际线程要求是什么。 就个人而言,我会使用一个Collections.synchronizedSet(新的HashSet()),因为它会很快…唯一的暗示是线程可能会产生时,否则他们不会有。
我发现这一点,我们可以使用Thread.holdsLock(Object obj)
来检查一个对象是否被locking:
当且仅当当前线程持有指定对象的监视器locking时返回
true
。
另一个解决方法是(如果你没有机会与这里给出的答案)正在使用超时。 即下面的一个会在挂起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; }
感谢这一点,它帮助我解决了一个竞争条件。 我改变了一点,穿上腰带和吊带。
所以这里是我对改进公认的答案的build议:
你可以通过做这样的事情来确保你可以安全地访问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!"); } }
这样可以避免在几乎同一时间调用tryLock()
的情况,导致返回值有可能被满足。 我想现在如果我错了,我可能会在这里过于谨慎。 但是,嘿! 我的演出现在是稳定的:-)
在我的博客阅读更多关于我的发展问题。