Java for Beginner
672 subscribers
541 photos
155 videos
12 files
827 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Недавнее обсуждение многопоточности в нашем чате, подтолкнуло меня восполнить недостатки знаний в этой области Java. 🧐

Дополняю темы про распространенные ошибки многопоточности.

Распространенные ошибки многопоточности

Race Condition (Состояние гонки)

Race Condition — это ситуация, когда поведение программы зависит от порядка или своевременности выполнения потоков. Оно возникает, когда несколько потоков одновременно обращаются к одному и тому же ресурсу (например, переменной или объекту), и хотя бы один из них изменяет его.

Пример Race Condition:
public class Counter {
private int count = 0;

public void increment() {
count++; // Неатомарная операция: чтение, увеличение, запись
}

public int getCount() {
return count;
}
}

public class RaceConditionExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();

Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});

Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});

t1.start();
t2.start();
t1.join();
t2.join();

System.out.println("Final count: " + counter.getCount()); // Ожидаем 2000, но результат может быть меньше
}
}


Почему Race Condition?

count++ не является атомарной операцией.

Она состоит из трех шагов:
Чтение текущего значения count.
Увеличение значения.
Запись нового значения обратно в переменную.


Если оба потока прочитают одно и то же значение до записи, итоговое значение будет некорректным.

Deadlock (Взаимная блокировка)

Deadlock — ситуация, при которой два или более потоков блокируют друг друга, ожидая освобождения ресурсов.

Пример Deadlock:
public class DeadlockExample {

private final Object lock1 = new Object();
private final Object lock2 = new Object();

public void method1() {
synchronized (lock1) {
System.out.println("Thread 1: Holding lock1...");
try { Thread.sleep(50); } catch (InterruptedException e) {}

synchronized (lock2) {
System.out.println("Thread 1: Acquired lock2.");
}
}
}

public void method2() {
synchronized (lock2) {
System.out.println("Thread 2: Holding lock2...");
try { Thread.sleep(50); } catch (InterruptedException e) {}

synchronized (lock1) {
System.out.println("Thread 2: Acquired lock1.");
}
}
}

public static void main(String[] args) {
DeadlockExample demo = new DeadlockExample();

Thread t1 = new Thread(demo::method1);
Thread t2 = new Thread(demo::method2);

t1.start();
t2.start();
}
}


Что происходит:
Поток 1 захватывает lock1 и ждет lock2.
Поток 2 захватывает lock2 и ждет lock1.
Оба потока застревают, ожидая освобождения ресурсов друг от друга.


#Java #Training #Multithreading #Medium #Race_Condition #Livelock #Starvation #Thread_Interference #Memory_Consistency_Errors #Multithreading_errors
Livelock (Живая блокировка)

Livelock похож на Deadlock, но здесь потоки не блокируются, а продолжают изменять свое состояние в попытке избежать конфликта, не продвигаясь дальше.

Пример Livelock:

public class LivelockExample {

static class Worker {
private boolean active = true;

public synchronized void work(Worker other) {
while (active) {
System.out.println(Thread.currentThread().getName() + " is working...");
try { Thread.sleep(50); } catch (InterruptedException e) {}

if (other.isActive()) {
System.out.println(Thread.currentThread().getName() + " is waiting...");
continue;
}
break;
}
}

public synchronized void setActive(boolean active) {
this.active = active;
}

public synchronized boolean isActive() {
return active;
}
}

public static void main(String[] args) {
Worker w1 = new Worker();
Worker w2 = new Worker();

Thread t1 = new Thread(() -> w1.work(w2), "Worker 1");
Thread t2 = new Thread(() -> w2.work(w1), "Worker 2");

t1.start();
t2.start();
}
}


Что происходит:
Оба потока продолжают "уступать" друг другу, не завершив работу.

Starvation (Голодание)

Starvation — ситуация, когда поток постоянно лишается доступа к ресурсу из-за того, что другие потоки с более высоким приоритетом занимают его.

Причина: Использование приоритетов потоков, где высокоприоритетные потоки блокируют низкоприоритетные.

Thread Interference (Конфликт потоков)

Это ситуация, похожая на Race Condition, когда несколько потоков одновременно читают и изменяют общие данные, приводя к непредсказуемым результатам.

Пример:
Потоки выполняют операции на одной переменной без синхронизации, что приводит к некорректным итоговым значениям.

Memory Consistency Errors (Ошибки согласованности памяти)

Эти ошибки возникают, когда один поток изменяет данные, но другие потоки видят устаревшее состояние этих данных.

Причина: Использование кэша процессора.
Переменная, измененная в одном потоке, может не быть видимой для других потоков.

Решение:
Использование volatile для обеспечения видимости изменений.
private static volatile boolean running = true;

public void run() {
while (running) {
// Выполняем задачу
}
}


Как предотвратить ошибки многопоточности

Использование синхронизации:
synchronized блоки или методы.
ReentrantLock для более гибкого управления блокировкой.


Использование атомарных типов:
Классы из пакета java.util.concurrent.atomic:
AtomicInteger, AtomicBoolean, AtomicReference.


Использование высокоуровневых утилит:
ExecutorService для управления потоками.
CountDownLatch, Semaphore, CyclicBarrier.


Избегание Deadlock:
Всегда захватывать блокировки в одном и том же порядке.
Использование таймаутов при ожидании захвата.


Проверка и отладка:
Инструменты отладки (например, jstack, VisualVM) для анализа состояния потоков.
Логирование текущих блокировок и их владельцев.


#Java #Training #Multithreading #Medium #Race_Condition #Livelock #Starvation #Thread_Interference #Memory_Consistency_Errors #Multithreading_errors