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

um6iljoc  于 2021-07-11  发布在  Java
关注(0)|答案(7)|浏览(260)

我有一个进程a,它在内存中包含一个表和一组记录(recorda、recordb等…)
现在,这个进程可以启动许多影响记录的线程,有时我们会有两个线程试图访问同一个记录-这种情况必须被拒绝。特别是如果一个记录被一个线程锁定,我希望另一个线程中止(我不想阻塞或等待)。
现在我做了这样的事情:

synchronized(record)
{
performOperation(record);
}

但这给我带来了麻烦。。。因为在process1执行操作时,如果process2进入,它将阻塞/等待synchronized语句,并且当process1完成时,它将执行操作。相反,我想要这样的东西:

if (record is locked)
   return;

synchronized(record)
{
performOperation(record);
}

关于如何做到这一点有什么线索吗?任何帮助都将不胜感激。谢谢,

f0brbegy

f0brbegy1#

需要注意的一点是,当你收到这样的信息时,它就过时了。换句话说,可以告诉您没有人拥有锁,但是当您尝试获取它时,会被阻塞,因为另一个线程在检查和您尝试获取它之间取出了锁。
布莱恩说得对 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 等待的时间很长,因此您可以尝试获取它十分之一秒,如果无法获取它,则中止(例如)。
(我认为很遗憾,据我所知,JavaAPI没有为“内置”锁定提供与 Monitor 类在.net中不存在。再说一次,在这两个平台中,当涉及到线程时,还有很多我不喜欢的东西——例如,每个对象都可能有一个监视器!)

f4t66c6m

f4t66c6m2#

看看Java5并发包中引入的锁对象。
例如

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

reentrantlock对象基本上与传统的 synchronized 机制,但有更多的功能。
编辑:正如乔恩所说 isLocked() 方法在那个时刻告诉您,然后该信息就过时了。trylock()方法将提供更可靠的操作(注意,您也可以在超时的情况下使用它)
编辑#2:示例现在包括 tryLock()/unlock() 为了清楚起见。

pwuypxnk

pwuypxnk3#

我找到了这个,我们可以用 Thread.holdsLock(Object obj) 要检查对象是否已锁定,请执行以下操作:
退货 true 当且仅当当前线程持有指定对象上的监视器锁时。
请注意 Thread.holdsLock() 退货 false 如果锁被某个东西持有,而调用线程不是持有锁的线程。

bkhjykvo

bkhjykvo4#

虽然上述使用锁定对象的方法是最好的方法,但如果必须能够使用监视器检查锁定,则可以这样做。但是,它确实附带了一个健康警告,因为该技术不能移植到非oraclejavavm上,而且它可能会在将来的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
  }
}

我的建议是,只有在无法重构代码以使用java.util.concurrent lock对象时,以及在oracle vm上运行时,才使用这种方法。

ux6nzvsh

ux6nzvsh5#

虽然锁的答案是非常好的,我想我会张贴一个替代使用不同的数据结构。基本上,您的各个线程都想知道哪些记录被锁定了,哪些没有。一种方法是跟踪锁定的记录,并确保数据结构具有将记录添加到锁定集的正确原子操作。
我将使用copyonwritearraylist作为一个例子,因为它不太“神奇”。copyonwritearrayset是一种更合适的结构。如果平均在同一时间锁定了很多记录,那么这些实现可能会影响性能。一个正确同步的哈希集也可以工作,并且锁是简短的。
基本上,使用代码如下所示:

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);
}

它使您不必为每个记录管理一个锁,并提供了一个单独的位置,以确保出于某种原因需要清除所有锁。另一方面,如果您曾经有过多个记录,那么一个真正的具有同步的hashset可能会做得更好,因为add/remove查找将是o(1)而不是线性的。
只是换个Angular 看问题。这取决于实际的线程需求。就我个人而言,我会使用collections.synchronizedset(newhashset()),因为它会非常快。。。唯一的含义是线程可能会屈服,否则它们就不会屈服。

sg24os4d

sg24os4d6#

另一个解决方法是(如果你没有机会得到这里给出的答案)使用超时。i、 e.下面的一个将在挂起1秒后返回空值:

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;
        }
iqih9akk

iqih9akk7#

谢谢你,它帮助我解决了一个比赛条件。我改了一点,把腰带和吊带都穿了。
因此,我建议对公认的答案进行改进:
你可以确保安全进入 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() 几乎同时,导致回报率可能被怀疑是充分的。如果我错了,我现在想说,我在这里可能过于谨慎了。但是嘿!我的工作现在稳定了:-)。。
在我的博客上阅读更多关于我的发展问题。

相关问题