锁定特定对象的Java线程 [英] Java threads locking on a specific object

查看:108
本文介绍了锁定特定对象的Java线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Web应用程序,我使用的是Oracle数据库,我的方法基本上是这样的:

I have a web application and I am using Oracle database and I have a method basically like this:

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个线程进入这个方法时都检查,当然还没有任何东西,然后它们都可以提交事务,创建一个重复的对象。

Right now there is no synchronization of any kind so n threads can of course access this method freely, the problem arises when 2 threads enter this method both check and of course there is nothing just yet, and then they can both commit the transaction, creating a duplicate object.

我不想在我的数据库中使用唯一的密钥标识符来解决这个问题,因为我认为我不应该捕获 SQLException

I do not want to solve this with a unique key identifier in my Database, because I don't think I should be catching that SQLException.

我也无法在提交之前检查,因为有几个检查不仅 1 ,这将花费相当长的时间。

I also cannot check right before the commit, because there are several checks not only 1, which would take a considerable amount of time.

我对锁和线程的体验有限,但我的想法基本上是将这个代码锁定在它接收的对象上。我不知道例如说我收到一个整数对象,并且我用值1锁定我的整数,这只会阻止具有值为1的另一个Integer的线程进入,而所有其他线程都阻止值!= 1 可以自由进入吗?这是它的工作原理吗?。

My experience with locks and threads is limited, but my idea is basically to lock this code on the object that it is receiving. I don't know if for example say I receive an Integer Object, and I lock on my Integer with value 1, would that only prevent threads with another Integer with value 1 from entering, and all the other threads with value != 1 can enter freely?, is this how it works?.

如果这是它的工作方式,那么它是怎么回事?锁对象比较?它是如何确定它们实际上是同一个对象的?关于这一点的好文章也将受到赞赏。

Also if this is how it works, how is the lock object compared? how is it determined that they are in fact the same object?. A good article on this would also be appreciated.

你会如何解决这个问题?

How would you solve this?.

推荐答案

你的想法是一个好的。这是简单/天真的版本,但它不太可行:

Your idea is a good one. This is the simplistic/naive version, but it's unlikely to work:

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,那么你需要同步整个方法:

This code uses the object itself as the lock. But it has to be the same object (ie objectInThreadA == objectInThreadB) if it's to work. If two threads are operating on an object that is a copy of each other - ie has the same "id" for example, then you'll need to either synchronize the whole method:

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

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

which will of course greatly reduce concurrency (throughput will drop to one thread at a time using the method - to be avoided).

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

Or find a way to get the same lock object based on the save object, like this approach:

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())才能正常工作。此外,getId()的数据类型可以是任何东西,包括原语(例如 int ),因为java的 autoboxing

This last version it the recommended one: It will ensure that two save objects that share the same "id" are locked with the same lock object - the method ConcurrentHashMap.putIfAbsent() is threadsafe, so "this will work", and it requires only that objectInThreadA.getId().equals(objectInThreadB.getId()) to work properly. Also, the datatype of getId() can be anything, including primitives (eg int) due to java's autoboxing.

如果覆盖<$ c $对于您的对象,c> equals()和 hashcode(),然后您可以使用对象本身而不是对象。 getId(),这将是一个改进(感谢@TheCapn指出这一点)

If you override equals() and hashcode() for your object, then you could use the object itself instead of object.getId(), and that would be an improvement (Thanks @TheCapn for pointing this out)

此解决方案仅适用于一个JVM 。如果您的服务器是集群的,那么整个不同的球类游戏和java的锁定机制将无法帮助您。您将不得不使用群集锁定解决方案,这超出了本答案的范围。

This solution will only work with in one JVM. If your servers are clustered, that a whole different ball game and java's locking mechanism will not help you. You'll have to use a clustered locking solution, which is beyond the scope of this answer.

这篇关于锁定特定对象的Java线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆