Здесь:
runnable -> new Thread(runnable).start()
Ключевым моментом, который делает ваш код многопоточным, является то, что вы вызываете метод start () ваших потоковых объектов. Если бы вы просто вызвали метод run класса потока, то вы фактически получили бы «включающий» поток, выполняющий всю работу.
Наконец, обратите внимание, что использование «голых» потоков напрямую не идеально. Об этом можно узнать, но Java предлагает важные абстракции, такие как ExecutorService, которые следует использовать вместо этого по разным причинам.
Основная причина, чтобы избежать необработанных потоков: вы должны контролировать все тонкие детали вручную. Сколько потоков следует использовать? Как насчет объединения и совместного использования потоков (создание потока сопряжено с большими накладными расходами, поэтому в реальном мире вы избегаете создания потоков для отдельных задач, которые затем выбрасывают их, как это делает ваш код). Помимо этого: как правило, вы хотите решить бизнес-проблему. Вы хотите использовать несколько потоков, чтобы предотвратить ситуации с горлышком бутылки. Пример: вы хотите делать несколько запросов по сети параллельно для получения и обработки данных. Тогда вы действительно заботитесь только о конечном результате, а не о тонкостях тонкости низкого уровня! Тогда вы, например, будете использовать объекты Future или CompleteableFuture.
Просто используйте поисковую систему и исследуйте эти термины, вы найдете много материала.