Thread.start(): примеры (JAVA)
Thread.start(): voidОписание метода Thread.start()
Метод Thread.start() предназначен для запуска нового потока выполнения. После вызова start() виртуальная машина Java создаёт отдельный поток, в котором вызывается метод run() данного объекта Thread. Метод не принимает аргументов и возвращает void.
Метод используется, когда необходимо выполнить код параллельно с основным потоком программы. Вызов start() может быть произведён только один раз для каждого экземпляра Thread. Повторный вызов приводит к исключению IllegalThreadStateException. Также нельзя запускать уже завершённый (dead) поток.
После вызова start() поток переходит в состояние Runnable, и планировщик потоков решает, когда он начнёт выполняться. Точное время начала зависит от операционной системы и загрузки процессора.
Примеры использования Thread.start()
Пример 1: простой поток с лямбда-выражением.
Thread thread = new Thread(() -> System.out.println("Выполняется новый поток"));
thread.start();Выполняется новый поток
Пример 2: поток с именем.
Thread namedThread = new Thread("Worker-1") {
public void run() {
System.out.println("Имя потока: " + getName());
}
};
namedThread.start();Имя потока: Worker-1
Пример 3: установка приоритета перед стартом.
Thread priorityThread = new Thread(() -> System.out.println("Приоритет: " + Thread.currentThread().getPriority()));
priorityThread.setPriority(Thread.MAX_PRIORITY);
priorityThread.start();Приоритет: 10
Пример 4: повторный вызов start() (приводит к ошибке).
Thread t = new Thread(() -> System.out.println("Один раз"));
t.start();
t.start(); // IllegalThreadStateExceptionОдин раз Exception in thread "main" java.lang.IllegalThreadStateException
Альтернативы Thread.start() в Java
Помимо прямого вызова start(), в Java существуют более высокоуровневые средства для работы с потоками:
- ExecutorService (например, Executors.newFixedThreadPool()) — управляет пулом потоков, принимает задачи Runnable или Callable. Предпочтительнее, когда требуется многократное выполнение задач без создания новых потоков каждый раз.
- ForkJoinPool — специализирован для рекурсивных задач, использует алгоритм воровства работы. Подходит для parallel stream и ForkJoinTask.
- CompletableFuture — для асинхронного программирования с цепочками callback. Не создаёт потоки напрямую, а использует общий ForkJoinPool.
- run() — вызов метода run() напрямую не создаёт новый поток, код выполняется в текущем потоке. Это не альтернатива, а типичная ошибка.
Выбор между ними зависит от задачи: для простого однократного запуска подходит start(), для пулов — ExecutorService, для асинхронных цепочек — CompletableFuture.
Аналоги Thread.start() в других языках
PHP: нет встроенной многопоточности, но есть расширение parallel. Пример:
$runtime = new parallel\Runtime();
$future = $runtime->run(function(){ echo "Поток PHP"; });Поток PHP
JavaScript: Web Workers (в браузере) или worker_threads (Node.js). Асинхронность с async/await не создаёт истинных потоков. Пример для Node.js:
const { Worker } = require('worker_threads');
new Worker('console.log("Поток JS")');Поток JS
Python: модуль threading. Пример:
import threading
t = threading.Thread(target=lambda: print("Поток Python"))
t.start()Поток Python
C#: аналогичный метод Start() у класса Thread. Пример:
new Thread(() => Console.WriteLine("Поток C#")).Start();Поток C#
Lua: корутины — кооперативные, не параллельные. Пример:
co = coroutine.create(function() print("Корутина Lua") end)
coroutine.resume(co)Корутина Lua
Go: горутины, запускаются ключевым словом go. Пример:
go func() { fmt.Println("Горутина Go") }()Горутина Go
Kotlin: корутины (GlobalScope.launch) или функция thread { ... } из kotlin.concurrent. Пример:
import kotlin.concurrent.thread
thread { println("Поток Kotlin") }Поток Kotlin
Отличие от Java: в Java Thread.start() создаёт системный поток (OS thread), в Kotlin функция thread делает то же самое, но корутины легче. В Go горутины значительно легче системных потоков, в Python потоки из-за GIL не дают параллелизма для CPU-bound задач.
Типичные ошибки при использовании Thread.start()
1. Повторный вызов start() после первого запуска — исключение IllegalThreadStateException.
Thread t = new Thread(() -> {});
t.start();
t.start(); // ошибкаException in thread "main" java.lang.IllegalThreadStateException
2. Вызов run() вместо start() — код выполняется в текущем потоке, новый поток не создаётся.
Thread t = new Thread(() -> System.out.println(Thread.currentThread().getName()));
t.run(); // main (текущий поток)main
3. Попытка стартовать уже завершённый поток — то же исключение.
4. Отсутствие синхронизации при доступе к общим данным — race condition, непредсказуемое поведение.
5. Использование устаревших методов Thread.stop(), suspend(), resume() — небезопасны, помечены как deprecated.
6. Deadlock — взаимная блокировка потоков, ожидающих освобождения ресурсов.
Изменения в методе Thread.start() в новых версиях Java
Сам метод start() не претерпел изменений в сигнатуре или поведении. Однако в разных версиях Java появлялись сопутствующие возможности:
- Java 5 — улучшенная модель памяти, добавлены пакет java.util.concurrent и ExecutorService.
- Java 8 — поддержка лямбда-выражений, что упростило создание потоков через start().
- Java 14 — улучшения в Thread.sleep (наносекундная точность), на start() не повлияло.
- Java 21 — введены виртуальные потоки (Virtual Threads). Появился новый метод Thread.startVirtualThread(Runnable), который запускает лёгкий виртуальный поток. Обычный start() продолжает работать как раньше, создавая платформенные потоки. Рекомендуется использовать виртуальные потоки для высоконагруженных приложений.
Таким образом, start() остаётся стабильным, но для новых проектов стоит рассмотреть виртуальные потоки как альтернативу.
Расширенные примеры использования Thread.start()
Пример 1: запуск потока с анонимным классом.
Thread thread = new Thread() {
public void run() {
System.out.println("Анонимный класс");
}
};
thread.start();Анонимный класс
Пример 2: запуск потока через реализацию Runnable.
class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable реализация");
}
}
new Thread(new MyRunnable()).start();Runnable реализация
Пример 3: использование ThreadFactory для настройки потока.
ThreadFactory factory = r -> {
Thread t = new Thread(r, "FactoryThread");
t.setDaemon(true);
return t;
};
Thread customThread = factory.newThread(() -> System.out.println("Создан фабрикой"));
customThread.start();Создан фабрикой
Пример 4: демон-поток.
Thread daemon = new Thread(() -> {
while (true) {
System.out.println("Демон работает");
try { Thread.sleep(1000); } catch (InterruptedException e) { break; }
}
});
daemon.setDaemon(true);
daemon.start();
Thread.sleep(3000);
System.out.println("Главный поток завершён");Демон работает Демон работает ... (примерно 3 раза) Главный поток завершён
Пример 5: ожидание завершения потока (join).
Thread worker = new Thread(() -> {
try { Thread.sleep(2000); } catch (InterruptedException e) {}
System.out.println("Работник завершён");
});
worker.start();
worker.join();
System.out.println("После join");Работник завершён После join
Пример 6: прерывание потока.
Thread sleeper = new Thread(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("Поток прерван");
}
});
sleeper.start();
sleeper.interrupt();Поток прерван
Пример 7: использование ThreadLocal для хранения данных потока.
ThreadLocal local = ThreadLocal.withInitial(() -> 0);
Thread t1 = new Thread(() -> {
local.set(1);
System.out.println(local.get());
});
Thread t2 = new Thread(() -> {
local.set(2);
System.out.println(local.get());
});
t1.start();
t2.start(); 1 2
Пример 8: множественные потоки с синхронизацией через synchronized.
class Counter {
private int count = 0;
public synchronized void increment() { count++; }
public int getCount() { return count; }
}
Counter counter = new Counter();
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) counter.increment();
});
threads[i].start();
}
for (Thread t : threads) t.join();
System.out.println(counter.getCount());10000