Вы можете использовать базу данных, чтобы сделать это, возможно даже со столбцом статуса, который, возможно, уже используется. Я не пытаюсь уладить все детали здесь ... просто чтобы передать идею. Огромная вещь здесь заключается в том, что этот механизм работает в потоках, процессах и даже машинах, в любой базе данных, с которой я когда-либо сталкивался, и вам больше нечего настраивать. Нет Redis и т. Д., Когда вы достигнете точки, где вы хотите масштабировать до нескольких экземпляров:
Вы должны создать другое состояние и использовать операцию проверки и установки:
public ServiceResponse update(ServiceRequest serviceRequest) {
....
while true:
String status = myDao.getStatus();
if (status.equals("COMPLETE")) {
return serviceError;
}
else if (status.equals("PROCESSING")) {
// do whatever you want to do if some other process or thread is
// already handling this. Maybe also return an error
return serviceError;
}
else if (myDao.testAndUpdateStatus(status, "PROCESSING")) {
// You would probably want to re-introduce a transaction here, maybe
// by moving this block to its own method. Sorry for cutting a
// corner here trying to just demonstrate the lock idea, which needs
// to not be in a transaction.
try {
myDao.insertRow();
myDao.updateStatus("COMPLETE");
sendAlert();
return serviceOK;
} catch (Exception ex) {
// What you do for the failure case will be very app specific. This
// is mostly to point out that you would want to set the status
// explicitly in the case of an error, to whatever is appropriate
myDao.updateStatus("COMPLETE")
return serviceError;
}
}
}
Вам нужно, чтобы операция блокировки не была транзакционной ... весь смысл в том, что каждая операция действительно атомарна. Если вы действительно нуждаетесь в транзакционной семантике, вы бы хотели, чтобы заключительный блок обработки был как-то включен в транзакцию. Я не пытаюсь получить транзакционную часть этого просто правильно. Я указываю на часто используемый способ управления синхронизацией и условиями гонки с использованием самой базы данных. Этот механизм выходит за пределы транзакции, которая запускается только после того, как поток вернул true
из testAndUpdateStatus
.
Ключевым моментом здесь является то, что testAndUpdateStatus()
будет только устанавливать статус и возвращать true
, если статус, переданный в качестве первого параметра, является текущим состоянием. В противном случае он ничего не сделает и вернет false
. Это решает условие состязания, когда один процесс проверяет состояние, но затем другой процесс также проверяет то же значение, прежде чем вы сможете установить состояние на "PROCESSING"
, в результате два процесса обрабатывают одно и то же обновление. Один из двух завершится ошибкой, потому что база данных не выполнит операцию, когда состояние больше не будет таким, как было, когда этот процесс прочитал значение.
Обратите внимание, что это будет работать не только в одном процессе, но и между процессами и даже машинами.