Вот как я обычно это делаю.
Вы можете создать очередь блокировки следующим образом:
LinkedBlockingQueue<String> files;
files = new LinkedBlockingQueue<String>(1000);
AtomicBoolean done = new AtomicBoolean(false);
Очередь может содержать только 1000 элементов, поэтому, если у вас есть миллиард файлов или что-то еще, вам не нужно беспокоиться о нехватке памяти. Вы можете изменить размер на любой, в зависимости от того, сколько памяти вы хотите занять.
В вашей основной теме вы делаете что-то вроде:
File directory = new File("path\to\folder");
for(File file : directory.listFiles()){
files.put(file.getAbsolutePath());
}
files.put(null);//this last entry tells the worker threads to stop
Функциональные блоки put до тех пор, пока в очереди не освободится место, поэтому при заполнении файлы прекратят чтение. Конечно, поскольку File.listFiles () на самом деле возвращает массив, а не коллекцию, которую не нужно загружать целиком в память, вы все равно заканчиваете тем, что загружаете полный список файлов в память, если используете эту функцию. Если это окажется проблемой, думаю, вам придется заняться чем-то другим.
Но эта модель также работает, если у вас есть какой-то другой метод перечисления файлов (например, если они все находятся в базе данных или что-то еще). Просто замените вызов directory.listFiles () тем, что вы используете, чтобы получить свой файл. список. Кроме того, если вам нужно обрабатывать файлы в подкаталогах, вам придется рекурсивно просматривать их, что может раздражать (но это решает проблему с памятью для очень больших каталогов)
затем в ваших рабочих потоках:
public void run(){
while(!done.get()){
String filename = files.take();
if(filename != null){
//do stuff with your file.
}
else{
done.set(true);//signal to the other threads that we found the final element.
}
}
}
Если все файлы в очереди были обработаны, take будет ждать, пока не появятся новые элементы.
В любом случае, это основная идея, этот код находится у меня в голове и не был протестирован в точности как есть.