你的想法很好。这是简单/天真的版本,但是不太可能起作用:
public static void saveSomethingimportantToDatabase(Object theObjectIwantToSave) { synchronized (theObjectIwantToSave) { if (!methodThatChecksThatObjectAlreadyExists) { storemyObject() //pseudo pre } // Have to do a lot other saving stuff, because it either saves everything or nothing commit() // pseudo pre 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()和
hashpre(),则可以使用对象本身代替
object.getId(),这将是一种改进(感谢@TheCapn指出这一点)
此解决方案只能在一个JVM中使用。如果您的服务器是群集的,那么完全不同的球类运动和Java的锁定机制将无济于事。您将不得不使用集群锁定解决方案,这超出了此答案的范围。



