← Назад к вопросам
В чем разница между submit в Executor и start в Thread?
2.0 Middle🔥 181 комментариев
#Многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# В чем разница между submit в Executor и start в Thread?
Краткий ответ
- thread.start() - прямой запуск одного потока (низкоуровневый)
- executor.submit() - отправка задачи в пул потоков для выполнения (высокоуровневый)
Executor лучше: переиспользует потоки, управляет ресурсами, обеспечивает Future.
Низкоуровневый способ: Thread
start() - создание нового потока
public class LowLevelExample {
public static void main(String[] args) {
// Каждый раз создаётся НОВЫЙ поток
Thread thread1 = new Thread(() -> {
System.out.println("Task 1 in " + Thread.currentThread().getName());
});
Thread thread2 = new Thread(() -> {
System.out.println("Task 2 in " + Thread.currentThread().getName());
});
thread1.start(); // Создание Thread 1
thread2.start(); // Создание Thread 2
// Результат:
// Task 1 in Thread-0
// Task 2 in Thread-1
// Два разных потока, каждый занимает ресурсы
}
}
Проблемы
- Неэффективно - создание потока дорого (~1 MB памяти)
- Нет контроля - потоки создаются бесконтрольно
- Нет результата - сложно получить результат выполнения
- Нет управления - нельзя остановить или переиспользовать
public class ThreadProblem {
public static void main(String[] args) throws Exception {
// Создание 1000 потоков - OutOfMemory!
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
try { Thread.sleep(1000); }
catch (InterruptedException e) {}
}).start();
}
}
}
Высокоуровневый способ: Executor
submit() - отправка в пул потоков
public class ExecutorExample {
public static void main(String[] args) throws Exception {
// Создаём пул с 2 потоками
ExecutorService executor = Executors.newFixedThreadPool(2);
// Отправляем 5 задач
for (int i = 1; i <= 5; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " in " +
Thread.currentThread().getName());
});
}
executor.shutdown();
// Результат:
// Task 1 in pool-1-thread-1
// Task 2 in pool-1-thread-2
// Task 3 in pool-1-thread-1 ← Переиспользует thread-1
// Task 4 in pool-1-thread-2 ← Переиспользует thread-2
// Task 5 in pool-1-thread-1 ← Снова thread-1
}
}
Преимущества
- Эффективно - переиспользует существующие потоки
- Контроль - точное количество потоков
- Future - результат и исключения
- Управление - shutdown, awaitTermination
Сравнение: start() vs submit()
| Аспект | start() | submit() |
|---|---|---|
| Уровень | Низкий (создание потока) | Высокий (пул потоков) |
| Эффективность | Низкая (новый поток) | Высокая (переиспользование) |
| Результат | Нет | Future<T> |
| Контроль | Минимум | Полный |
| Ресурсы | ~1 MB на поток | Шаринг ресурсов |
| Масштабируемость | Плохая | Отличная |
Пример: 1000 задач
С Thread.start() - ПЛОХО
public class BadApproach {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
System.out.println("Task in " +
Thread.currentThread().getName());
}).start();
}
Thread.sleep(1000);
long elapsed = System.currentTimeMillis() - start;
System.out.println("Время: " + elapsed + " ms");
// Результат: создано 1000 потоков, медленно, OutOfMemory риск
}
}
С Executor.submit() - ХОРОШО
public class GoodApproach {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
// 10 потоков на 1000 задач
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executor.submit(() -> {
System.out.println("Task in " +
Thread.currentThread().getName());
});
}
executor.shutdown();
executor.awaitTermination(10, TimeUnit.SECONDS);
long elapsed = System.currentTimeMillis() - start;
System.out.println("Время: " + elapsed + " ms");
// Результат: только 10 потоков, быстро, контролируемо
}
}
Future - главное преимущество submit()
С Thread.start() - нет результата
Thread thread = new Thread(() -> {
int result = 2 + 2; // Вычислили
// Как получить результат из main потока?
});
thread.start();
// Нет способа получить result!
С Executor.submit() - есть Future
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(() -> {
return 2 + 2; // Вычислили
});
// Получаем результат
try {
Integer result = future.get(); // 4
System.out.println("Результат: " + result);
} catch (ExecutionException e) {
System.out.println("Ошибка: " + e.getCause());
}
executor.shutdown();
Типы ExecutorService
newFixedThreadPool(n)
ExecutorService executor = Executors.newFixedThreadPool(5);
// 5 потоков всегда, очередь неограниченная
newSingleThreadExecutor()
ExecutorService executor = Executors.newSingleThreadExecutor();
// 1 поток, задачи выполняются последовательно
newCachedThreadPool()
ExecutorService executor = Executors.newCachedThreadPool();
// Динамическое количество потоков (0 до max)
// Хорошо для короткие задачи
newScheduledThreadPool(n)
ScheduledExecutorService executor =
Executors.newScheduledThreadPool(5);
// Для периодического выполнения
executor.scheduleAtFixedRate(() -> {
System.out.println("Каждые 5 секунд");
}, 0, 5, TimeUnit.SECONDS);
Обработка результатов
Future.get() - блокирует
Future<String> future = executor.submit(() -> {
Thread.sleep(1000);
return "Result";
});
// get() ЖДЁТ завершения
String result = future.get(); // Блокирует до 1000 ms
Проверка isDone()
Future<String> future = executor.submit(() -> {
return "Result";
});
while (!future.isDone()) {
System.out.println("Ещё выполняется...");
Thread.sleep(100);
}
String result = future.get(); // Уже готово
Обработка исключений
Future<Integer> future = executor.submit(() -> {
throw new RuntimeException("Ошибка!");
});
try {
Integer result = future.get();
} catch (ExecutionException e) {
// e.getCause() - оригинальное исключение
System.out.println("Ошибка: " + e.getCause());
}
Best Practices
❌ Плохо
// Создаём потоки каждый раз
for (int i = 0; i < tasks.size(); i++) {
new Thread(tasks.get(i)).start();
}
✅ Хорошо
// Используем пул потоков
ExecutorService executor =
Executors.newFixedThreadPool(10);
for (Runnable task : tasks) {
executor.submit(task);
}
executor.shutdown();
Выводы
- Thread.start() - для единичных потоков (редко)
- Executor.submit() - для множественных задач (всегда)
- Executor управляет пулом потоков эффективно
- Future позволяет получить результат
- ExecutorService лучше масштабируется
- Правило: Используй Executor для всего, кроме исключительных случаев
- В production почти всегда Executor, а не Thread