Многопоточность в Java: потоки, синхронизация, wait/notify и concurrent блокировки
Это полное руководство по многопоточности в Java с примерами потоков, синхронизации, wait/notify, concurrent блокировок и семафоров.
Коротко
Многопоточность позволяет выполнять задачи параллельно. Синхронизация гарантирует согласованный доступ к данным, а concurrent блокировки предоставляют современные механизмы управления потоками.
Определение
Многопоточность – это способность программы одновременно выполнять несколько потоков. Каждый поток имеет собственный stack, но они всем делят heap памяти.
Основные концепции:
Жизненный цикл потока
- NEW: поток создан, но не запущен
- RUNNABLE: готов к выполнению (выполняется или ждёт планировщика)
- BLOCKED: ожидает блокировки монитора
- WAITING: ожидает сигнала условия без временного ограничения
- TIMED_WAITING: ожидает с временным ограничением
- TERMINATED: поток завершён
Механизмы синхронизации
- synchronized: синхронизация на основе монитора
- wait()/notify(): коммуникация между потоками
- ReentrantLock: гибкий механизм блокировки
- ReadWriteLock: отдельные блокировки для чтения и записи
- Semaphore: управление доступом через счётчик
- CountDownLatch: ожидание завершения нескольких потоков
- CyclicBarrier: точка синхронизации потоков
Потокобезопасность
- Immutable Objects: безопасны по своей природе
- Thread-Local: данные, специфичные для потока
- Volatile: видимость значений переменных между потоками
- Atomic Classes: операции без блокировок
Ключевые моменты
- Потоки: лёгкие процессы со своим stack
- Синхронизация: защита общих ресурсов
- Монитор: синхронизационный механизм на уровне объекта
- wait/notify: обмен сигналами между потоками
- Deadlock: взаимная блокировка
- Race Condition: неконтролируемый доступ к общим данным
- Volatile: гарантирует видимость изменений между потоками
- Производительность: важна для параллельных приложений
Основные компоненты
- Управление потоками: создание, управление, жизненный цикл
- Синхронизация: synchronized, блокировки, семафоры
- Коммуникация между потоками: wait/notify, blocking queues
- Потокобезопасные коллекции: concurrent структуры данных
- Пулы потоков: ExecutorService, ThreadPoolExecutor
- Потокобезопасность: immutable объекты, volatile, atomic классы
- Производительность: гранулярность блокировок, снижение contention
- Отладка: thread dump, race conditions, deadlocks
Примеры кода
1. Базовые операции с потоками
public class ThreadGrundlagen {
// Поток через наследование от Thread
static class MeinThread extends Thread {
private String name;
public MeinThread(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(name + " - Зählung: " + i);
try {
Thread.sleep(500); // 500ms пауза
} catch (InterruptedException e) {
System.out.println(name + " был прерван");
return;
}
}
System.out.println(name + " завершён");
}
}
// Поток через реализацию Runnable
static class MeinRunnable implements Runnable {
private String name;
public MeinRunnable(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
System.out.println(name + " - Работа: " + i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
System.out.println(name + " прерван");
return;
}
}
System.out.println(name + " готов");
}
}
// Lambda-выражение как Runnable
static void lambdaThreadDemo() {
Thread lambdaThread = new Thread(() -> {
for (int i = 1; i <= 3; i++) {
System.out.println("Lambda поток - Шаг " + i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
return;
}
}
});
lambdaThread.start();
}
public static void main(String[] args) {
System.out.println("=== Базовые операции с потоками ===");
// Поток через наследование
MeinThread thread1 = new MeinThread("Thread-1");
thread1.start();
// Поток через Runnable
Thread thread2 = new Thread(new MeinRunnable("Runnable-1"));
thread2.start();
// Lambda поток
lambdaThreadDemo();
// Ожидание завершения потоков
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Главный поток завершён");
}
}
2. Синхронизация с synchronized
public class SynchronisationDemo {
// Общий ресурс
static class Zaehler {
private int wert = 0;
// Синхронизированный метод
public synchronized void erhoehen() {
wert++;
System.out.println(Thread.currentThread().getName() +
" увеличивает на: " + wert);
}
// Синхронизированный блок
public void verringern() {
synchronized(this) {
wert--;
System.out.println(Thread.currentThread().getName() +
" уменьшает на: " + wert);
}
}
public synchronized int getWert() {
return wert;
}
}
// Producer-Consumer с wait/notify
static class Warenlager {
private final int[] lager = new int[5];
private int index = 0;
public synchronized void einlagern(int ware) throws InterruptedException {
// Ждём, если хранилище полно
while (index >= lager.length) {
System.out.println("Хранилище полно - производитель ждёт");
wait();
}
lager[index] = ware;
index++;
System.out.println(Thread.currentThread().getName() +
" добавил: " + ware);
// Уведомляем потребителя
notifyAll();
}
public synchronized int auslagern() throws InterruptedException {
// Ждём, если хранилище пусто
while (index <= 0) {
System.out.println("Хранилище пусто - потребитель ждёт");
wait();
}
index--;
int ware = lager[index];
System.out.println(Thread.currentThread().getName() +
" извлёк: " + ware);
// Уведомляем производителя
notifyAll();
return ware;
}
}
static class Producer implements Runnable {
private Warenlager lager;
public Producer(Warenlager lager) {
this.lager = lager;
}
@Override
public void run() {
try {
for (int i = 1; i <= 10; i++) {
lager.einlagern(i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
static class Consumer implements Runnable {
private Warenlager lager;
public Consumer(Warenlager lager) {
this.lager = lager;
}
@Override
public void run() {
try {
for (int i = 1; i <= 10; i++) {
lager.auslagern();
Thread.sleep(150);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public static void main(String[] args) {
System.out.println("=== Демонстрация синхронизации ===");
// Простая синхронизация
Zaehler zaehler = new Zaehler();
Thread[] threads = new Thread[5];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 3; j++) {
zaehler.erhoehen();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
return;
}
}
});
threads[i].setName("Thread-" + i);
}
// Запускаем потоки
for (Thread t : threads) {
t.start();
}
// Ожидаем завершения потоков
for (Thread t : threads) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Финальное значение: " + zaehler.getWert());
// Демонстрация Producer-Consumer
System.out.println("\n=== Демонстрация Producer-Consumer ===");
Warenlager lager = new Warenlager();
Thread producer = new Thread(new Producer(lager), "Producer");
Thread consumer = new Thread(new Consumer(lager), "Consumer");
producer.start();
consumer.start();
try {
producer.join();
consumer.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3. Concurrent Locks и современная синхронизация
import java.util.concurrent.locks.*;
import java.util.concurrent.*;
public class ConcurrentLocksDemo {
// ReentrantLock Beispiel
static class Bankkonto {
private double kontostand;
private final ReentrantLock lock = new ReentrantLock();
public Bankkonto(double startbetrag) {
this.kontostand = startbetrag;
}
public void einzahlen(double betrag) {
lock.lock();
try {
double alterStand = kontostand;
Thread.sleep(50); // Simuliere Verarbeitung
kontostand = alterStand + betrag;
System.out.println(Thread.currentThread().getName() +
" eingezahlt: " + betrag +
", neuer Stand: " + kontostand);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
public boolean abheben(double betrag) {
lock.lock();
try {
if (kontostand >= betrag) {
double alterStand = kontostand;
Thread.sleep(50);
kontostand = alterStand - betrag;
System.out.println(Thread.currentThread().getName() +
" abgehoben: " + betrag +
", neuer Stand: " + kontostand);
return true;
} else {
System.out.println(Thread.currentThread().getName() +
" Konnte nicht abheben: " + betrag +
" (Stand: " + kontostand + ")");
return false;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
} finally {
lock.unlock();
}
}
public double getKontostand() {
lock.lock();
try {
return kontostand;
} finally {
lock.unlock();
}
}
}
// ReadWriteLock Beispiel
static class ThreadSafeList {
private final List<String> liste = new ArrayList<>();
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
public void add(String element) {
writeLock.lock();
try {
liste.add(element);
System.out.println(Thread.currentThread().getName() +
" hinzugefügt: " + element);
} finally {
writeLock.unlock();
}
}
public String get(int index) {
readLock.lock();
try {
return liste.get(index);
} finally {
readLock.unlock();
}
}
public List<String> getAll() {
readLock.lock();
try {
return new ArrayList<>(liste); // Kopie zurückgeben
} finally {
readLock.unlock();
}
}
public int size() {
readLock.lock();
try {
return liste.size();
} finally {
readLock.unlock();
}
}
}
// Semaphore Beispiel
static class RessourcenPool {
private final Semaphore semaphore;
private final List<String> ressourcen;
public RessourcenPool(int maxRessourcen) {
semaphore = new Semaphore(maxRessourcen);
ressourcen = new ArrayList<>();
for (int i = 1; i <= maxRessourcen; i++) {
ressourcen.add("Ressource-" + i);
}
}
public String acquire() throws InterruptedException {
semaphore.acquire();
synchronized(ressourcen) {
if (!ressourcen.isEmpty()) {
String ressource = ressourcen.remove(0);
System.out.println(Thread.currentThread().getName() +
" acquired: " + ressource);
return ressource;
}
}
semaphore.release();
return null;
}
public void release(String ressource) {
synchronized(ressourcen) {
ressourcen.add(ressource);
System.out.println(Thread.currentThread().getName() +
" released: " + ressource);
}
semaphore.release();
}
}
// CountDownLatch Beispiel
static class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
private final int workerId;
public Worker(CountDownLatch startSignal, CountDownLatch doneSignal, int workerId) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
this.workerId = workerId;
}
@Override
public void run() {
try {
// Warten auf Startsignal
System.out.println("Worker " + workerId + " bereit");
startSignal.await();
// Arbeit ausführen
System.out.println("Worker " + workerId + " arbeitet");
Thread.sleep((long) (Math.random() * 1000));
System.out.println("Worker " + workerId + " fertig");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
doneSignal.countDown();
}
}
}
public static void main(String[] args) {
System.out.println("=== Concurrent Locks Demo ===");
// ReentrantLock Demo
Bankkonto konto = new Bankkonto(1000.0);
Thread[] bankThreads = new Thread[4];
for (int i = 0; i < bankThreads.length; i++) {
final int threadId = i;
bankThreads[i] = new Thread(() -> {
for (int j = 0; j < 3; j++) {
if (threadId % 2 == 0) {
konto.einzahlen(100);
} else {
konto.abheben(50);
}
}
}, "BankThread-" + i);
}
for (Thread t : bankThreads) {
t.start();
}
for (Thread t : bankThreads) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Endkontostand: " + konto.getKontostand());
// ReadWriteLock Demo
System.out.println("\n=== ReadWriteLock Demo ===");
ThreadSafeList liste = new ThreadSafeList();
// Writer Thread
Thread writer = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
liste.add("Element-" + i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
return;
}
}
}, "Writer");
// Reader Threads
Thread[] readers = new Thread[3];
for (int i = 0; i < readers.length; i++) {
readers[i] = new Thread(() -> {
for (int j = 0; j < 10; j++) {
List<String> alle = liste.getAll();
System.out.println(Thread.currentThread().getName() +
" gelesen: " + alle.size() + " Elemente");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
return;
}
}
}, "Reader-" + i);
}
writer.start();
for (Thread reader : readers) {
reader.start();
}
try {
writer.join();
for (Thread reader : readers) {
reader.join();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
// Semaphore Demo
System.out.println("\n=== Semaphore Demo ===");
RessourcenPool pool = new RessourcenPool(2);
Thread[] poolThreads = new Thread[5];
for (int i = 0; i < poolThreads.length; i++) {
poolThreads[i] = new Thread(() -> {
try {
String ressource = pool.acquire();
if (ressource != null) {
Thread.sleep(1000); // Ressource nutzen
pool.release(ressource);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "PoolThread-" + i);
}
for (Thread t : poolThreads) {
t.start();
}
for (Thread t : poolThreads) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// CountDownLatch Demo
System.out.println("\n=== CountDownLatch Demo ===");
int workerCount = 3;
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(workerCount);
for (int i = 1; i <= workerCount; i++) {
new Thread(new Worker(startSignal, doneSignal, i)).start();
}
try {
Thread.sleep(1000);
System.out.println("Alle Worker bereit - Startsignal!");
startSignal.countDown();
doneSignal.await();
System.out.println("Alle Worker fertig!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
ReentrantLock предоставляет явное управление блокировками с полной переносимостью в отличие от встроенного synchronized. Посмотрите на пример банковского счета: мы защищаем изменения баланса блокировкой, гарантируя, что операции не перекрываются. Вызовы lock() и unlock() в блоке try-finally обеспечивают безопасное освобождение даже при исключениях.
ReadWriteLock решает задачу по-другому. Когда много потоков читают одни и те же данные, а пишет только один, блокировка на чтение позволяет им работать параллельно. Писатель же получает исключительный доступ. В примере с ThreadSafeList три читателя могут одновременно получать снимок списка, пока писатель медленно добавляет элементы.
Semaphore контролирует доступ к ресурсам, ограничивая число потоков, работающих с ними одновременно. Это похоже на очередь у парковки с фиксированным числом мест. Каждый acquire() ждет, пока освободится место, а release() сообщает остальным, что ресурс больше не нужен.
CountDownLatch используется для синхронизации нескольких потоков в одной точке. Главный поток создает лatch с числом, равным количеству рабочих, и ждет на await(). Каждый рабочий по завершении вызывает countDown(). Когда счетчик достигает нуля, основной поток продолжает работу. Это удобно для стартовых сигналов и сбора результатов.
4. Потокобезопасные структуры данных и Atomic классы
import java.util.concurrent.atomic.*;
import java.util.concurrent.*;
public class ThreadSafeCollections {
// Atomic Classes Demo
static class AtomicZaehler {
private final AtomicInteger zaehler = new AtomicInteger(0);
private final AtomicLong longZaehler = new AtomicLong(0);
private final AtomicBoolean flag = new AtomicBoolean(false);
private final AtomicReference<String> nachricht = new AtomicReference<>("");
public void increment() {
int alterWert = zaehler.getAndIncrement();
System.out.println(Thread.currentThread().getName() +
" increment: " + alterWert + " -> " + zaehler.get());
}
public void add(long wert) {
long alterWert = longZaehler.getAndAdd(wert);
System.out.println(Thread.currentThread().getName() +
" add: " + alterWert + " + " + wert + " -> " + longZaehler.get());
}
public void toggleFlag() {
boolean alterWert = flag.getAndSet(!flag.get());
System.out.println(Thread.currentThread().getName() +
" toggle: " + alterWert + " -> " + flag.get());
}
public void updateNachricht(String neueNachricht) {
String alteNachricht = nachricht.getAndSet(neueNachricht);
System.out.println(Thread.currentThread().getName() +
" update: '" + alteNachricht + "' -> '" + neueNachricht + "'");
}
public int getZaehler() { return zaehler.get(); }
public long getLongZaehler() { return longZaehler.get(); }
public boolean getFlag() { return flag.get(); }
public String getNachricht() { return nachricht.get(); }
}
// Concurrent Collections Demo
static class ConcurrentCollectionsDemo {
public static void hashMapDemo() {
System.out.println("=== ConcurrentHashMap Demo ===");
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// Writer Threads
Thread[] writers = new Thread[3];
for (int i = 0; i < writers.length; i++) {
final int threadId = i;
writers[i] = new Thread(() -> {
for (int j = 0; j < 5; j++) {
String key = "Key-" + threadId + "-" + j;
map.put(key, threadId * 100 + j);
System.out.println(Thread.currentThread().getName() +
" put: " + key);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
return;
}
}
}, "Writer-" + i);
}
// Reader Thread
Thread reader = new Thread(() -> {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() +
" size: " + map.size());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
return;
}
}
}, "Reader");
// Запускаем все потоки
reader.start();
for (Thread writer : writers) {
writer.start();
}
// Ждем завершения потоков
try {
for (Thread writer : writers) {
writer.join();
}
reader.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final map size: " + map.size());
}
public static void blockingQueueDemo() {
System.out.println("\n=== BlockingQueue Demo ===");
BlockingQueue<String> queue = new ArrayBlockingQueue<>(5);
// Producer
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 10; i++) {
String item = "Item-" + i;
queue.put(item);
System.out.println("Producer put: " + item +
" (queue size: " + queue.size() + ")");
Thread.sleep(200);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Producer");
// Consumer
Thread consumer = new Thread(() -> {
try {
for (int i = 1; i <= 10; i++) {
String item = queue.take();
System.out.println("Consumer take: " + item +
" (queue size: " + queue.size() + ")");
Thread.sleep(300);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Consumer");
producer.start();
consumer.start();
try {
producer.join();
consumer.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// ThreadLocal Demo
static class ThreadLocalDemo {
private static ThreadLocal<Integer> threadLocalValue = ThreadLocal.withInitial(() -> 100);
private static ThreadLocal<String> threadLocalName = new ThreadLocal<>();
public static void demo() {
System.out.println("=== ThreadLocal Demo ===");
Thread[] threads = new Thread[3];
for (int i = 0; i < threads.length; i++) {
final int threadId = i;
threads[i] = new Thread(() -> {
threadLocalName.set("Thread-" + threadId);
for (int j = 0; j < 3; j++) {
int wert = threadLocalValue.get();
String name = threadLocalName.get();
System.out.println(name + " wert: " + wert);
// Изменяем значение ThreadLocal
threadLocalValue.set(wert + threadId);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
return;
}
}
// Очищаем ThreadLocal
threadLocalName.remove();
threadLocalValue.remove();
}, "Thread-" + i);
}
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
// Atomic Classes Demo
System.out.println("=== Atomic Classes Demo ===");
AtomicZaehler zaehler = new AtomicZaehler();
Thread[] atomicThreads = new Thread[4];
for (int i = 0; i < atomicThreads.length; i++) {
final int threadId = i;
atomicThreads[i] = new Thread(() -> {
zaehler.increment();
zaehler.add(threadId * 10);
zaehler.toggleFlag();
zaehler.updateNachricht("Nachricht von Thread-" + threadId);
}, "AtomicThread-" + i);
}
for (Thread t : atomicThreads) {
t.start();
}
for (Thread t : atomicThreads) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Final values:");
System.out.println("Zaehler: " + zaehler.getZaehler());
System.out.println("LongZaehler: " + zaehler.getLongZaehler());
System.out.println("Flag: " + zaehler.getFlag());
System.out.println("Nachricht: " + zaehler.getNachricht());
// Concurrent Collections Demo
ConcurrentCollectionsDemo.hashMapDemo();
ConcurrentCollectionsDemo.blockingQueueDemo();
// ThreadLocal Demo
ThreadLocalDemo.demo();
}
}
Thread-Pools и ExecutorService
ThreadPoolExecutor
// Fixed Thread Pool
ExecutorService fixedPool = Executors.newFixedThreadPool(4);
// Cached Thread Pool
ExecutorService cachedPool = Executors.newCachedThreadPool();
// Single Thread Executor
ExecutorService singlePool = Executors.newSingleThreadExecutor();
// Scheduled Thread Pool
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);
// Tasks ausführen
Future<String> future = fixedPool.submit(() -> {
Thread.sleep(1000);
return "Ergebnis";
});
// Scheduled Tasks
scheduledPool.scheduleAtFixedRate(() -> {
System.out.println("Periodische Aufgabe");
}, 0, 1, TimeUnit.SECONDS);
// Pool herunterfahren
fixedPool.shutdown();
scheduledPool.shutdown();
Избежание Deadlock
Условия возникновения Deadlock
- Mutual Exclusion: ресурс может использоваться только одним потоком
- Hold and Wait: поток удерживает ресурсы и ждёт другие
- No Preemption: ресурсы невозможно отобрать принудительно
- Circular Wait: циклическая цепочка ожидания между потоками
Стратегии предотвращения
// Lock Ordering - всегда захватывать блокировки в одном порядке
public void transfer(Account from, Account to, double amount) {
// Синхронизируем аккаунты в детерминированном порядке
Account first = from.getId() < to.getId() ? from : to;
Account second = from.getId() < to.getId() ? to : from;
synchronized(first) {
synchronized(second) {
from.withdraw(amount);
to.deposit(amount);
}
}
}
// TryLock с timeout
public boolean transferWithTryLock(Account from, Account to, double amount) {
while (true) {
try {
if (from.getLock().tryLock(1, TimeUnit.SECONDS)) {
try {
if (to.getLock().tryLock(1, TimeUnit.SECONDS)) {
try {
from.withdraw(amount);
to.deposit(amount);
return true;
} finally {
to.getLock().unlock();
}
}
} finally {
from.getLock().unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
// Небольшая пауза перед следующей попыткой
Thread.sleep(100);
}
}
Оптимизация производительности
Гранулярность блокировок
// Грубая синхронизация (плохо)
public synchronized void addElement(Object element) {
// Вся операция заблокирована
list.add(element);
size++;
}
// Тонкая синхронизация (лучше)
public void addElement(Object element) {
synchronized(list) {
list.add(element);
}
synchronized(this) {
size++;
}
}
Volatile vs Synchronized
// Volatile для простой видимости переменной
private volatile boolean running = true;
public void stop() {
running = false; // Видно всем потокам
}
public void run() {
while (running) {
// Выполнить работу
}
}
// Synchronized для сложных операций
private int counter = 0;
public synchronized void increment() {
counter++; // Атомарная операция
}
Преимущества и недостатки
Преимущества многопоточности
- Производительность: параллельное выполнение на многоядерных системах
- Отзывчивость: интерфейс остаётся реактивным во время длительных операций
- Использование ресурсов: лучшая загрузка системных ресурсов
- Масштабируемость: задачи можно распределять между потоками
Недостатки
- Сложность: синхронизация подвержена ошибкам
- Отладка: race conditions сложно воспроизвести
- Накладные расходы: создание потоков и переключение контекста требуют времени
- Ресурсы: повышенное потребление памяти и CPU
Типичные вопросы на собеседовании
-
Какая разница между wait() и sleep()? wait() освобождает блокировку, sleep() удерживает её. wait() требует synchronized, sleep() нет.
-
Объясните Deadlock и способы его предотвращения! Deadlock это взаимная блокировка потоков. Предотвращение через Lock Ordering и TryLock с timeout.
-
Когда использовать volatile вместо synchronized? volatile для простой видимости переменных, synchronized для сложных операций.
-
Чем Runnable отличается от Callable? Runnable не возвращает результат, Callable возвращает результат и может генерировать исключения.
Основные источники
- https://docs.oracle.com/javase/tutorial/essential/concurrency/
- https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html
- https://www.baeldung.com/java-concurrency



