Skip to content

12 Что такое race condition? Что такое deadLock? Что такое live-lock?

В многопоточном программировании важно понимать различные проблемы, которые могут возникнуть при взаимодействии потоков. Три из таких проблем — это race condition, deadlock и live-lock. Давайте рассмотрим каждую из них подробнее.

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 thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

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

        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();

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

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

Deadlock

Deadlock (взаимная блокировка) — это ситуация, когда два или более потоков блокируют друг друга, ожидая освобождения ресурсов, которые они уже захватили. В результате ни один из потоков не может продолжить выполнение.

Пример:

public class DeadlockExample {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread 1: Holding lock 1...");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("Thread 1: Waiting for lock 2...");
                synchronized (lock2) {
                    System.out.println("Thread 1: Acquired lock 2!");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread 2: Holding lock 2...");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("Thread 2: Waiting for lock 1...");
                synchronized (lock1) {
                    System.out.println("Thread 2: Acquired lock 1!");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

В этом примере, если Thread 1 захватывает lock1 и ожидает lock2, а Thread 2 захватывает lock2 и ожидает lock1, оба потока окажутся в состоянии блокировки.

Live-lock

Live-lock — это ситуация, когда потоки продолжают изменять свое состояние, но не могут завершить выполнение, потому что они постоянно реагируют на действия друг друга. В отличие от deadlock, потоки не блокируются, но не могут продвинуться вперед.

Пример:

public class LiveLockExample {
    private static class Person {
        private String name;
        private boolean wantsToTalk;

        public Person(String name) {
            this.name = name;
            this.wantsToTalk = true;
        }

        public void talkTo(Person other) {
            while (this.wantsToTalk) {
                System.out.println(this.name + " wants to talk to " + other.name);
                this.wantsToTalk = false; // Отказ от разговора
                other.wantsToTalk = true; // Другой хочет поговорить
            }
        }
    }

    public static void main(String[] args) {
        Person alice = new Person("Alice");
        Person bob = new Person("Bob");

        Thread thread1 = new Thread(() -> alice.talkTo(bob));
        Thread thread2 = new Thread(() -> bob.talkTo(alice));

        thread1.start();
        thread2.start();
    }
}

В этом примере, оба человека хотят поговорить, но они постоянно отказываются от разговора, что приводит к ситуации live-lock.

Заключение

  • Race Condition: Проблема, возникающая из-за однов