Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/372.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何确定对象是否已锁定(同步),以便在Java中不被阻止?_Java_Synchronization - Fatal编程技术网

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

如何确定对象是否已锁定(同步),以便在Java中不被阻止?,java,synchronization,Java,Synchronization,我有一个进程a,它在内存中包含一个表和一组记录(recordA、recordB等) 现在,这个进程可以启动影响记录的多个线程,有时我们可以有两个线程尝试访问同一个记录-这种情况必须被拒绝。特别是,如果一个记录被一个线程锁定,我希望另一个线程中止(我不想阻塞或等待) 目前我做的事情如下: synchronized(record) { performOperation(record); } if (record is locked) return; synchronized(record)

我有一个进程a,它在内存中包含一个表和一组记录(recordA、recordB等)

现在,这个进程可以启动影响记录的多个线程,有时我们可以有两个线程尝试访问同一个记录-这种情况必须被拒绝。特别是,如果一个记录被一个线程锁定,我希望另一个线程中止(我不想阻塞或等待)

目前我做的事情如下:

synchronized(record)
{
performOperation(record);
}
if (record is locked)
   return;

synchronized(record)
{
performOperation(record);
}
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);
}
但这给我带来了麻烦。。。因为当Process1执行该操作时,如果Process2进入,它将阻塞/等待同步语句,并且当Process1完成时,它将执行该操作。相反,我想要这样的东西:

synchronized(record)
{
performOperation(record);
}
if (record is locked)
   return;

synchronized(record)
{
performOperation(record);
}
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);
}
关于如何做到这一点,有什么线索吗? 任何帮助都将不胜感激。 谢谢,

看看Java 5并发程序包中引入的对象

e、 g

该对象基本上与传统的
同步
机制做相同的事情,但功能更多

编辑:正如Jon所指出的那样,
isLocked()
方法会在那一刻告诉您,此后该信息就过时了。该方法将提供更可靠的操作(注意,您也可以将其与超时一起使用)


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

需要注意的一点是,当您收到此类信息时,它就过时了。换句话说,可以告诉您没有人拥有锁,但是当您尝试获取它时,您会阻塞,因为另一个线程在支票和您尝试获取它之间取出了锁

Brian正确地指出了
,但我认为您真正想要的是它的方法:

您还可以调用
tryLock
,等待一段时间,因此您可以尝试获取它十分之一秒,如果无法获取,则中止(例如)


(我认为很遗憾,据我所知,Java API没有为“内置”锁定提供与.NET中的
Monitor
类相同的功能。再说一次,在这两种平台中,在线程方面,我都不喜欢很多其他东西——例如,每个对象都可能有一个监视器!)

虽然锁的答案非常好,但我想我应该发布一个使用不同数据结构的替代方案。本质上,您的各个线程都想知道哪些记录被锁定了,哪些没有被锁定。一种方法是跟踪锁定的记录,并确保数据结构具有向锁定集添加记录的正确原子操作

我将使用CopyOnWriteArrayList作为一个例子,因为它不太“神奇”。CopyOnWriteArraySet是一种更合适的结构。如果您平均在同一时间锁定了很多记录,那么这些实现可能会影响性能。正确同步的哈希集也可以工作,并且锁很简单

基本上,使用代码如下所示:

synchronized(record)
{
performOperation(record);
}
if (record is locked)
   return;

synchronized(record)
{
performOperation(record);
}
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);
}
CopyOnWriteArrayList lockedRecords=。。。。
...
如果(!lockedRecords.addIfAbsent(记录))
返回;//未获得锁,记录已被锁定
试一试{
//录制唱片
}        
最后{
锁定记录。删除(记录);
}
它使您不必为每个记录管理一个锁,并提供了一个单独的位置,以便在出于某种原因需要清除所有锁时使用。另一方面,如果您曾经有过多个记录,那么带有同步的实际哈希集可能会做得更好,因为添加/删除查找将是O(1),而不是线性的


只是一种不同的看待事物的方式。这取决于实际的线程需求。就我个人而言,我会使用Collections.synchronizedSet(newHashSet()),因为它会非常快。。。唯一的含义是线程可能会在其他情况下不会产生的情况下产生。虽然上述使用锁对象的方法是最好的方法,但如果您必须能够使用监视器检查锁,则可以这样做。但是,它确实附带了一个健康警告,因为该技术无法移植到非Oracle Java虚拟机上,而且它可能会在未来的虚拟机版本中崩溃,因为它不是受支持的公共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上运行,才使用这种方法。

我发现,我们可以使用以下方法检查对象是否被锁定:

当且仅当当前线程持有指定对象上的监视器锁时,返回
true

请注意,
Thread.holdsLock()
返回
false
,如果锁由某个对象持有,而调用线程不是持有锁的线程。

另一种解决方法是(如果您没有机会获得这里给出的答案)使用超时。i、 e.挂起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;
        }
ExecutorService executor=Executors.newSingleThreadExecutor();
//为线程创建一个可调用的
Future futureTask=executor.submit(新的可调用(){
@凌驾
公共字符串调用()引发异常{
返回myObject.getSomething();
}
});
试一试{
返回futureTask.get(1000,时间单位为毫秒);
}捕获(InterruptedException | ExecutionException | TimeoutException e){
//对象已被锁定,请检查异常类型
返回null;
}

感谢您的帮助,它帮助我解决了一个竞赛条件。我把它改了一点,系上腰带和吊带

下面是我对公认答案的改进建议:

通过执行以下操作,可以确保安全访问
tryLock()
方法:

synchronized(record)
{
performOperation(record);
}
if (record is locked)
   return;

synchronized(record)
{
performOperation(record);
}
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);
}
Lock localLock=new ReentrantLock();