Обратите внимание, что вы будете платить за этот счетчик с хитами производительности.Фактически вам требуется барьер памяти для каждого запроса, если вы используете параллельные потоки для обслуживания запросов.(Инструкция по барьеру памяти обычно стоит до ~ 200 инструкций.)
По вашему вопросу также кажется, что вам даже не нужен счетчик, а скорее двоичный флаг, который указывает, есть ли какие-либо активные запросы, например,requestsInProgress
флаг.Идея состоит в том, что вы «корректно завершаете работу», когда значение флага равно false
.
Если ваш контейнер в основном предоставляет конечные точки сети, например REST / HTTP, тогда я настоятельно рекомендую вам рассмотреть NIO и использовать однопоточный механизм диспетчеризации длялинеаризовать req / rep на периферии контейнера.(Вы можете поставить их в очередь и развернуть в N потоков обработки, используя параллельные очереди в java.util.concurrent
.
[NIO subsystem] <-{poll}-[Selector(accept/read)/dispatch thread] => [Q:producer/consumer pattern 1:N]
[NIO subystem] <-{poll}-[Selector(write)/responder thread] <= [Q:producer/consumer N:1]
Преимущество?
Если вы используете один и тот же поток для отправки и ответчика, то памяти нетБарьеры задействованы - поток будет прикреплен к ядру, а ваш флаг будет эксклюзивным для его строки кэша:
например
после запроса очередей отправки: инкремент req_in_progress
после того, как ответчик удаляет ответ: декремент req_in_progress
При выключении будет возникать необходимость в синхронизации общей памяти, но это намного лучше, чем затраты на каждый запрос, так как вы платите только тогда, когда вы на самом деле платитеэто нужно.
Если производительность вообще не является проблемой, то почему бы просто не использовать AtomicInteger
для счетчика и поместить его в глобальный контекст?