← Назад к вопросам

В чем разница между fixed thread pool и cached thread pool?

2.0 Middle🔥 151 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

В чем разница между fixed thread pool и cached thread pool

Это популярный вопрос о java.util.concurrent.Executors. Оба используются для управления потоками, но имеют разные стратегии и применяются в разных сценариях.

Таблица сравнения

ПараметрFixed Thread PoolCached Thread Pool
Количество потоковФиксированноеДинамическое (растёт по мере надобности)
Максимум потоковnThreadsInteger.MAX_VALUE
Очередь задачНеограниченная (LinkedBlockingQueue)Синхронная очередь (SynchronousQueue)
Переиспользование потоковДа (существуют постоянно)Да (60 секунд idle timeout)
Когда использоватьИзвестное кол-во задачМного коротких асинхронных задач
Утечка потоковДа, если не shutdownНет (потоки умирают после 60с)

Fixed Thread Pool

Создаёт пул с фиксированным количеством потоков:

ExecutorService executor = Executors.newFixedThreadPool(5);
// Всегда 5 потоков, не больше, не меньше

Как это работает

Создание:
ExecutorService = Executors.newFixedThreadPool(3)
                                      ↓
ThreadPoolExecutor(
    corePoolSize = 3,
    maximumPoolSize = 3,
    keepAliveTime = 0,
    TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>()
)

Визуализация:
┌─────────────────────────────────────┐
│         Fixed Thread Pool (3)       │
├─────────────────────────────────────┤
│  Thread-1   Thread-2   Thread-3     │
│  (working)  (idle)     (working)    │
├─────────────────────────────────────┤
│     Queue (LinkedBlockingQueue)     │
│  [Task4] [Task5] [Task6] [...]      │
└─────────────────────────────────────┘

Пример

public class FixedThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);
        
        // Отправляем 10 задач
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("Task " + taskId + " in " + 
                    Thread.currentThread().getName());
                try {
                    Thread.sleep(2000);  // Имитируем работу
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
        
        // Вывод:
        // Task 0 in pool-1-thread-1
        // Task 1 in pool-1-thread-2
        // Task 2 in pool-1-thread-3
        // [Ждём 2 сек]
        // Task 3 in pool-1-thread-1  <- переиспользуется
        // Task 4 in pool-1-thread-2  <- переиспользуется
        // Task 5 in pool-1-thread-3  <- переиспользуется
        // ...
        
        executor.shutdown();
        executor.awaitTermination(10, TimeUnit.SECONDS);
    }
}

Проблемы с Fixed Thread Pool

// Проблема 1: Очередь растёт без ограничений
ExecutorService executor = Executors.newFixedThreadPool(2);

for (int i = 0; i < 1000000; i++) {
    executor.submit(() -> {
        // Долгая операция
        Thread.sleep(60000);
    });
}
// LinkedBlockingQueue заполняется 999998 задачами!
// OutOfMemoryError через какое-то время

Cached Thread Pool

Создаёт пул, который растёт по мере надобности:

ExecutorService executor = Executors.newCachedThreadPool();
// Количество потоков зависит от количества задач

Как это работает

Создание:
ExecutorService = Executors.newCachedThreadPool()
                                      ↓
ThreadPoolExecutor(
    corePoolSize = 0,
    maximumPoolSize = Integer.MAX_VALUE,
    keepAliveTime = 60,
    TimeUnit.SECONDS,
    new SynchronousQueue<Runnable>()
)

Визуализация (в начале):
┌─────────────────────────────────────┐
│      Cached Thread Pool             │
├─────────────────────────────────────┤
│         (пусто)                     │
├─────────────────────────────────────┤
│     Queue (SynchronousQueue - пусто) │
└─────────────────────────────────────┘

После submit() 5 задач:
┌─────────────────────────────────────┐
│      Cached Thread Pool (5)         │
├─────────────────────────────────────┤
│  T-1      T-2      T-3      T-4  T-5│
│  (work)   (work)   (work)   (wait) │
├─────────────────────────────────────┤
│     Queue (SynchronousQueue)        │
│          (пусто)                    │
└─────────────────────────────────────┘

Пример

public class CachedThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        
        // Отправляем 10 коротких задач
        long start = System.currentTimeMillis();
        
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("Task " + taskId + " in " + 
                    Thread.currentThread().getName() + " started");
                try {
                    Thread.sleep(100);  // Быстрая задача
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("Task " + taskId + " completed");
            });
        }
        
        // Вывод (примерно):
        // Task 0 in pool-1-thread-1 started
        // Task 1 in pool-1-thread-2 started
        // Task 2 in pool-1-thread-3 started
        // ... все 10 потоков создаются параллельно
        // [Ждём 100ms]
        // Task 0 completed
        // Task 1 completed
        // ... все завершаются параллельно
        
        executor.shutdown();
        executor.awaitTermination(10, TimeUnit.SECONDS);
        
        long elapsed = System.currentTimeMillis() - start;
        System.out.println("Total time: " + elapsed + "ms");  // ~100ms
    }
}

Преимущество SynchronousQueue

// SynchronousQueue - это особая очередь без ёмкости
// Producer и Consumer должны встретиться

SynchronousQueue<String> queue = new SynchronousQueue<>();

// Поток 1:
queue.put("hello");  // Блокируется, пока кто-то не возьмёт

// Поток 2:
String msg = queue.take();  // Берёт "hello", разблокирует поток 1

В ThreadPoolExecutor это означает:

  • Если потока нет и есть задача -> создать новый поток (не ждать в очереди)
  • Задача не накапливается в очереди

Сравнение в действии

public class ComparisonExample {
    
    static void testFixedPool() throws Exception {
        System.out.println("=== Fixed Thread Pool ===");
        ExecutorService executor = Executors.newFixedThreadPool(2);
        
        long start = System.currentTimeMillis();
        
        for (int i = 0; i < 4; i++) {
            final int id = i;
            executor.submit(() -> {
                System.out.println("Task " + id + " in " + 
                    Thread.currentThread().getName());
                try { Thread.sleep(500); } catch (Exception e) {}
            });
        }
        
        executor.shutdown();
        executor.awaitTermination(5, TimeUnit.SECONDS);
        
        System.out.println("Time: " + 
            (System.currentTimeMillis() - start) + "ms");
        // Output: Time: 1000ms (tasks выполняются в 2 batch'а по 2)
    }
    
    static void testCachedPool() throws Exception {
        System.out.println("=== Cached Thread Pool ===");
        ExecutorService executor = Executors.newCachedThreadPool();
        
        long start = System.currentTimeMillis();
        
        for (int i = 0; i < 4; i++) {
            final int id = i;
            executor.submit(() -> {
                System.out.println("Task " + id + " in " + 
                    Thread.currentThread().getName());
                try { Thread.sleep(500); } catch (Exception e) {}
            });
        }
        
        executor.shutdown();
        executor.awaitTermination(5, TimeUnit.SECONDS);
        
        System.out.println("Time: " + 
            (System.currentTimeMillis() - start) + "ms");
        // Output: Time: 500ms (все 4 задачи параллельно)
    }
}

Когда использовать

Fixed Thread Pool

// 1. Server с известным количеством connections
ExecutorService executor = Executors.newFixedThreadPool(100);
// Максимум 100 одновременных подключений

// 2. Batch processing
ExecutorService executor = Executors.newFixedThreadPool(8);
// 8 CPU cores - процесс 8 файлов одновременно

// 3. Когда нужен контроль над ресурсами
int numThreads = Runtime.getRuntime().availableProcessors();
ExecutorService executor = Executors.newFixedThreadPool(numThreads);

Cached Thread Pool

// 1. Много коротких асинхронных задач
ExecutorService executor = Executors.newCachedThreadPool();
for (Request req : requests) {
    executor.submit(() -> handleRequest(req));  // Быстро
}

// 2. REST API сервис (много I/O)
ExecutorService executor = Executors.newCachedThreadPool();
// На каждый request создаём поток (на 60 сек)

// 3. Когда нет максимума задач
executor.submit(() -> blockingCall());

Проблемы и решения

Problem: Cached Pool может создать слишком много потоков

// Плохо: миллион быстрых HTTP requests
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 1000000; i++) {
    executor.submit(() -> httpRequest());  // Миллион потоков!
}

// Решение: используй ThreadPoolExecutor с ограничением
ExecutorService executor = new ThreadPoolExecutor(
    10,                    // corePoolSize
    100,                   // maximumPoolSize
    60, TimeUnit.SECONDS,  // keepAliveTime
    new LinkedBlockingQueue<>(1000)  // Ограниченная очередь
);

Problem: Fixed Pool очередь может расти бесконечно

// Плохо: задачи добавляются быстрее, чем выполняются
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i < 1000000; i++) {
    executor.submit(() -> slowOperation());
}
// LinkedBlockingQueue переполнится -> OutOfMemoryError

// Решение: используй RejectedExecutionHandler
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2, 2,
    0, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<>(10),
    new ThreadPoolExecutor.CallerRunsPolicy()  // Выполни в caller потоке
);

Итог

  • Fixed Thread Pool: когда знаешь максимум задач и нужен контроль над памятью
  • Cached Thread Pool: когда много коротких асинхронных задач и нужна эффективность

В production обычно используют ThreadPoolExecutor напрямую с правильными параметрами вместо готовых фабрик Executors, чтобы иметь полный контроль над поведением.

В чем разница между fixed thread pool и cached thread pool? | PrepBro