Ответ
Потоки зависят от количества ядер, потому что ядра процессора физически выполняют инструкции. Каждое ядро может обрабатывать один поток одновременно.
Если ядер больше — система может параллельно выполнять больше потоков без переключения контекста. Например, на 4-ядерном процессоре 4 потока будут работать действительно параллельно, а на 2-ядерном — только 2, остальные будут выполняться с переключением.
// Пример: оптимальное кол-во потоков для CPU-bound задач
val cores = Runtime.getRuntime().availableProcessors()
val executor = Executors.newFixedThreadPool(cores)
Но это не значит, что потоков не может быть больше ядер — система использует вытесняющую многозадачность, переключая потоки на ядрах. Однако слишком много потоков приведет к накладным расходам на переключение.
Ответ 18+ 🔞
Э, слушай, вот объясняю как есть, без этих ваших заумных терминов. Потоки, ядра... это всё, блядь, как очередь в столовую, ёпта. Представь: у тебя есть повара — это ядра. Один повар — одно ядро. Он может готовить один заказ — один поток — за раз. Всё просто, как три копейки.
Если поваров четыре, то они могут четыре котлеты жарить одновременно, реально параллельно. А если повар один, а заказов пять, то он будет метаться между сковородками, переключаться. Вроде и работает, но эффективность — пиздец, ноль. То же самое с процессором: на 4-ядрах 4 потока — красота, летают. На 2-ядрах 4 потока — уже начинается цирк с переключениями, контекст меняется, овердохуища накладных расходов.
Но это не значит, что потоков больше ядер делать нельзя. Можно, конечно! Система-то хитрая жопа, она будет эти потоки на те же ядра набрасывать, быстро-быстро между ними перепрыгивая. Только вот если ты сделаешь потоков, как говна за баней, то все ресурсы уйдут не на работу, а на это самое переключение — «ой, ты постой, а ты теперь работай». В итоге всё встанет колом, производительность накроется медным тазом.
Вот, смотри на код, тут всё честно:
// Пример: оптимальное кол-во потоков для CPU-bound задач
val cores = Runtime.getRuntime().availableProcessors()
val executor = Executors.newFixedThreadPool(cores)
Видишь? Берём количество ядер (availableProcessors()) и создаём пул ровно на столько потоков. Для задач, которые процессор грузят по полной (CPU-bound), это часто самый разумный ход. Не умничай, не выдумывай число с потолка. А то наделаешь потоков — овердохуища, а толку — хуй с горы. Всё будет еле шевелиться, потому что процессор будет только и делать, что бздеть, переключая контексты, вместо нормальной работы.