Коммиты должны быть атомарными, что означает, что они должны быть как можно более мелкими, но при этом самодостаточными. Если у вас не получается коммитить небольшими порциями, не ломая при этом работоспособность ветки, то ваш код не является чистым.
Множества - это не "рандомный список". Почитайте. Нужны они для быстрых операций с их членами - пересечением, объединением, разностью. У Скиены, если мне не изменяет память, был пример оценки скорости поиска пересечения. Для множеств из миллиона элементов это заняло 0,02 микросекунды, для соразмерных списков 15 минут.
Потому, что Arrays.asList() возвращает экземпляр ArrayList, использующий переданный массив. То есть вы те же самые данные просто обернули дополнительной абстракцией.
На национальность и гражданство могут обращать внимание там, где на одну вакансию десять соискателей, а когда работаешь в позиции, где на одного соискателя десять оферов, то не помешает даже похабная татуировка на лице. Так что всё только от вас зависит.
Выглядит как обычный striped lock. Берёте обычную очередь, обычный пул потоков и заполняете массив экземплярами Lock. Поток в начале работы берёт Message из очереди, получает из него SubjectId, вычисляет его хэш и пытается захватить блокировку из соответствующего хэшу элемента массива. Если блокировка удалась, поток выполняет свою работу. Если нет, возвращает Message в конец очереди и берёт следующий из начала. Остаётся только подобрать эффективный размер массива блокировок.
Перед завершение программы необходимо передавать потокам какой-либо сигнал о завершений, который они будут проверять на каждой итерации цикла и завершать его в случае необходимости, а закрытие файлов сделать после цикла.