Ваша идея хорошая.Это упрощенная / наивная версия, но она вряд ли будет работать:
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), если он должен работать.Если два потока работают над объектом, который является копией друг друга - то есть, например, с одним и тем же "идентификатором", то вам нужно будет либо синхронизировать весь метод:
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
}
Эта последняя версия является рекомендованной: она гарантирует, что два объекта сохранения, которые имеют один и тот же идентификатор, заблокированы одним и тем же объектом блокировки.метод ConcurrentHashMap.putIfAbsent()
является потокобезопасным, поэтому «это будет работать», и для правильной работы требуется только, чтобы objectInThreadA.getId().equals(objectInThreadB.getId())
.Кроме того, тип данных getId () может быть любым, включая примитивы (например, int
) из-за автобоксирования в java .
Если вы переопределяете equals()
и hashcode()
для вашего объекта, тогда вы можете использовать сам объект вместо object.getId()
, и это было бы улучшением (спасибо @TheCapn за указание на это)
Это решение будет работать только в одной JVM.Если ваши серверы сгруппированы, то совершенно другая игра с мячом и механизм блокировки java вам не помогут.Вам придется использовать решение кластерной блокировки, которое выходит за рамки этого ответа.