当前位置: 首页 > 面试题库 >

锁定特定对象的Java线程

卢磊
2023-03-14
问题内容

我有一个Web应用程序,并且正在使用Oracle数据库,并且有一种基本上像这样的方法:

public static void saveSomethingImportantToDataBase(Object theObjectIwantToSave) {
      if (!methodThatChecksThatObjectAlreadyExists) {
         storemyObject() //pseudo code
     }
     // Have to do a lot other saving stuff, because it either saves everything or nothing
     commit() // pseudo code to actually commit all my changes to the database.
}

现在没有任何类型的同步,因此n个线程当然可以自由地访问此方法,当2个线程都进入此方法都进行检查并且当然还没有任何东西时,就会出现问题,然后它们都可以提交事务并创建一个重复的对象。

我不想在数据库中使用唯一的密钥标识符来解决此问题,因为我认为我不应该抓住它SQLException

我也不能在提交之前进行检查,因为不仅要进行多项检查1,而且要花费大量时间。

我对锁和线程的经验是有限的,但是我的想法基本上是将代码锁定在它所接收的对象上。我不知道是否例如说我收到一个Integer对象,并且将我的Integer锁定为值1,那是否只能阻止具有另一个Integer的值为1的线程进入,而所有其他具有该值的线程value != 1可以自由进入呢?这是怎么运作的?

同样,如果这是如何工作的,那么如何比较锁对象?如何确定它们实际上是同一对象?对此的好文章也将不胜感激。

您将如何解决?


问题答案:

你的想法很好。这是简单/天真的版本,但是不太可能起作用:

public static void saveSomethingImportantToDataBase(Object theObjectIwantToSave) {
    synchronized (theObjectIwantToSave) {
        if (!methodThatChecksThatObjectAlreadyExists) {
            storemyObject() //pseudo code
        }
        // Have to do a lot other saving stuff, because it either saves everything or nothing
        commit() // pseudo code to actually commit all my changes to the database.
    }
}

此代码将对象本身用作锁。但是它必须是 相同的 对象(即objectInThreadA ==
objectInThreadB)才能起作用。如果两个线程正在一个互为 副本 的对象上运行-例如,具有相同的“ id”,则您将需要同步整个方法:

    public static synchronized void saveSomethingImportantToDataBase(Object theObjectIwantToSave) ...

当然,这将大大减少并发性(使用该方法,吞吐量将一次下降至一个线程-避免使用)。

或者找到一种基于保存对象获取 相同 锁定对象的方法,例如这种方法:

private static final ConcurrentHashMap<Object, Object> LOCKS = new ConcurrentHashMap<Object, Object>();
public static void saveSomethingImportantToDataBase(Object theObjectIwantToSave) {
    synchronized (LOCKS.putIfAbsent(theObjectIwantToSave.getId(), new Object())) {
        ....    
    }
    LOCKS.remove(theObjectIwantToSave.getId()); // Clean up lock object to stop memory leak
}

最后一个版本是推荐的版本:它将确保使用相同的锁定对象锁定共享相同“ id”的两个保存对象-
该方法ConcurrentHashMap.putIfAbsent()是线程安全的,因此“此方法将起作用”,并且仅要求该对象objectInThreadA.getId().equals(objectInThreadB.getId())才能正常工作。同样,int由于Java的autoboxing,getId()的数据类型可以是任何类型,包括原始类型(例如)。

如果为对象覆盖equals()hashcode(),则可以使用对象本身代替object.getId(),这将是一种改进(感谢@TheCapn指出这一点)

此解决方案只能在一个JVM中使用。如果您的服务器是群集的,那么完全不同的球类运动和Java的锁定机制将无济于事。您将不得不使用集群锁定解决方案,这超出了此答案的范围。



 类似资料:
  • 问题内容: 以下函数在其自己的线程中执行: 当执行到达该行时,将引发异常: 有谁知道如何锁定对象或函数以防止并发?我试图添加一个Lock对象: 和线 在功能开始时,但是没有用。 问题答案: 为了在一个对象上调用wait(),您必须在该对象上持有同步锁(尽管该锁实际上是在线程等待时释放的): 我必须承认,在这种情况下, 为什么 您要这样做让我感到困惑…

  • 我想为strerror\u r调用创建线程本地缓冲区,并编写自己的线程安全字符*my\u strerror(int),它将使用线程本地缓冲区并调用strerror\r。 当阅读R. Stevens在Unix环境高级编程中关于pthread_getspecific()的例子时,我觉得差异-为什么在下面的例子中使用互斥锁? 书中的例子:

  • 在示例代码中 在这个页面上, lock1和lock2分别控制c1和c2上的更新。 然而, 正在获取对象lock1的锁并在同步块时释放它 被执行。 当这个代码块被执行时,这个对象的成员c1上可能还有一个更新——我看不出这个更新是如何被代码中的lock1上的同步所阻止的。 只有对象lock1可以独占访问——除此之外别无它物(?) 那么,实施情况如何 在上面的代码中不同于 甚至 当c1是一个对象而不是一

  • 假设我有两条线。Thread1正在访问一个同步方法,同时,Thread2正在访问同一对象的另一个同步方法。据我所知,Thread2应该等到Thread1完成它的任务。我的问题是,Thread2是否在对象的等待线程列表中?对我来说似乎是这样,但Thread2不调用wait()方法,那么作为逻辑结果,它不应该在对象的等待线程列表中。如果它不在对象的等待线程列表中,那么Thread2的状态是什么?

  • 我正在修改一个Java服务器软件。整个应用程序是单线程的。我的一个更改需要很多时间,所以我决定异步执行,以避免冻结主线程。 问题是:这种锁在Java中的最佳实现是什么?例如,我是否应该使用亲自完成。 编辑:看看我当前实现的答案。

  • Java多线程中“私有最终对象”锁定有什么用? 就我的理解而言,我认为要使一个类成为线程安全的,我们要么使用内在锁定,要么将所有方法标记为同步的 例如,使用内在锁定的代码: } 我们可以用以下外在锁替换上面的代码: 是否建议使用上述外部锁而不是使用内部锁定使类作为线程安全?如果我的理解在这里有误,请纠正我?