如何确定对象是否被锁定(同步)以便不在Java中阻塞?

Sha*_*n00 71 java synchronization

我有一个进程A,它在内存中包含一组带有一组记录的表(recordA,recordB等......)

现在,这个进程可以启动许多影响记录的线程,有时我们可以有2个线程试图访问同一条记录 - 这种情况必须被拒绝.特别是如果一个记录被一个线程锁定,我希望另一个线程中止(我不想BLOCK或WAIT).

目前我这样做:

synchronized(record)
{
performOperation(record);
}
Run Code Online (Sandbox Code Playgroud)

但是这导致了我的问题......因为当Process1正在执行操作时,如果Process2进入,则阻塞/等待同步语句,并且当Process1完成时,它执行操作.相反,我想要这样的东西:

if (record is locked)
   return;

synchronized(record)
{
performOperation(record);
}
Run Code Online (Sandbox Code Playgroud)

有关如何实现这一目标的任何线索?任何帮助将非常感激.谢谢,

Jon*_*eet 77

有一点要注意的是,即时你收到这样的信息,它已经过期.换句话说,你可能会被告知没有人拥有锁,但是当你试图获取它时,你会阻止,因为另一个线程取出了检查之间的锁定并试图获取它.

布莱恩是正确的指向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
}
Run Code Online (Sandbox Code Playgroud)

你也可以花tryLock一些时间等待 - 所以你可以尝试获取它十分之一秒,然后如果你不能得到它就中止(例如).

(我认为遗憾的是,Java API没有 - 据我所知 - 为"内置"锁定提供相同的功能,就像Monitor.NET中的类一样.然后,有很多在涉及到线程时,我在两个平台上都不喜欢其他的东西 - 例如,每个对象都有一个监视器!)

  • 但是我如何使用每个记录锁?目前,记录存储在HashTable记录中......所以我需要一个匹配的Hashtable of Locks?我试图确保我有最大的并发性,所以如果一个进程想要访问应该没问题的recordC(如果只有recordB被锁定) - 我使用全局LOCK然后它基本上与锁定整个哈希表相同.......有道理吗? (5认同)

Bri*_*new 23

看一下Java 5并发包中引入的Lock对象.

例如

Lock lock = new ReentrantLock()
if (lock.tryLock()) {
   try {
      // do stuff using the lock...
   }
   finally {
      lock.unlock();
   }
}
   ...
Run Code Online (Sandbox Code Playgroud)

ReentrantLock的对象基本上是在做同样的事情,传统synchronized机制,但有更多的功能.

编辑:正如乔恩所指出的,该isLocked()方法在那一刻告诉你,此后该信息已经过时.该的tryLock()方法会给更可靠的操作(注意,您可以使用此与超时以及)

编辑#2:tryLock()/unlock()为清楚起见,现在包括示例.


小智 8

虽然使用Lock对象的上述方法是最好的方法,但如果必须能够使用监视器检查锁定,则可以完成.但是,它确实附带了健康警告,因为该技术无法移植到非Oracle Java VM,并且在未来的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
  }
}
Run Code Online (Sandbox Code Playgroud)

我的建议是只有在你不能重构你的代码以使用java.util.concurrent Lock对象以及你是否在Oracle VM上运行时才使用这种方法.

  • @alestanis,我不同意.在这里,我今天正在阅读这些答案,并对所有答案/评论感到高兴,无论何时给出. (12认同)
  • @alestanis,我认为这很不幸 - 我经常看到旧问题有一个可接受的答案,但也有更新,更好的答案,因为技术改变或因为接受的答案实际上并不完全正确. (2认同)

Rah*_*tha 7

我发现了这个,我们可以Thread.holdsLock(Object obj)用来检查对象是否被锁定:

true当且仅当当前线程在指定对象上保存监视器锁时返回.

请注意,Thread.holdsLock()返回false如果锁被持有的东西,并调用线程是不是持有锁的线程.