Это далеко не простая задача. Мне пришлось написать довольно много кода для его достижения (к сожалению, он написан на Clojure, поэтому примеров Java-кода под рукой нет). Основной принцип - иметь поточно-ориентированную ссылку на ваш IndexSearcher, которая доступна как для кода чтения индекса, так и для кода построения индекса. Построитель индекса начинает создавать новый индекс в фоновом режиме; это не мешает существующим индексам. По завершении он входит в синхронизированный блок, закрывает IndexReader и IndexSearcher, открывает новый IndexReader и обновляет глобальную ссылку IndexSearcher на созданный из него IndexSearcher. Весь читающий код должен синхронизироваться с той же блокировкой, что и упомянутый синхронизированный блок. Лучшая альтернатива - использовать ReentrantReadWriteLock вместо синхронизированного блока. Это позволит избежать ненужных конфликтов между многими потоками читателей.
После инициализации, во время нормальной работы, вы можете использовать NRTManager для одновременного чтения индекса и внесения в него инкрементных обновлений.