Статьи

Как избежать тупика в потоках Java?

Как избежать тупика в Java? Это один из самых популярных вопросов интервью на Java, посвященный многопоточности, который задают в основном на старшем уровне и задают множество вопросов. Хотя вопрос выглядит очень простым, но большинство разработчиков Java застряли, когда вы начнете углубляться.

Вопросы интервью начинаются с «Что такое тупик?»

Ответ прост, когда два или более потоков ждут друг друга, чтобы освободить ресурс, который им нужен (блокировка) и застряли на бесконечное время, ситуация называется тупиком. Это произойдет только в случае многозадачности или многопоточности .

Как вы обнаруживаете тупик в Java?

Хотя это может иметь много ответов, моя версия — сначала, я бы посмотрел код, если я вижу вложенный синхронизированный блок или вызываю один синхронизированный метод из другого, или пытаюсь получить блокировку для другого объекта, тогда есть хороший шанс тупика если разработчик не очень осторожен.

Другой способ — найти его, когда вы фактически заблокированы во время работы приложения, попробуйте сделать дамп потока, в Linux вы можете сделать это командой «kill -3», это выведет на экран статус всех потоков в файле журнала приложения. и вы можете увидеть, какой поток заблокирован на каком объекте.

Вы можете проанализировать этот дамп потока с помощью таких инструментов, как fastthread.io, который позволяет загружать дамп потока и анализировать его.

Другой способ — использовать jConsole / VisualVM , он покажет вам, какие потоки заблокированы и на каком объекте.

Написать программу на Java, которая приведет к тупику?

Как только вы ответите на предыдущий вопрос, они могут попросить вас написать код, который приведет к тупику в Java?

Вот одна из моих версий:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
 * Java program to create a deadlock by imposing circular wait.
 *
 * @author WINDOWS 8
 *
 */
public class DeadLockDemo {
 
    /*
     * This method request two locks, first String and then Integer
     */
    public void method1() {
        synchronized (String.class) {
            System.out.println("Aquired lock on String.class object");
 
            synchronized (Integer.class) {
                System.out.println("Aquired lock on Integer.class object");
            }
        }
    }
 
    /*
     * This method also requests same two lock but in exactly
     * Opposite order i.e. first Integer and then String.
     * This creates potential deadlock, if one thread holds String lock
     * and other holds Integer lock and they wait for each other, forever.
     */
    public void method2() {
        synchronized (Integer.class) {
            System.out.println("Aquired lock on Integer.class object");
 
            synchronized (String.class) {
                System.out.println("Aquired lock on String.class object");
            }
        }
    }
}

Если method1 () и method2 () оба будут вызваны двумя или несколькими потоками, существует высокая вероятность взаимоблокировки, потому что если поток 1 получает блокировку для объекта Sting при выполнении method1 (), а поток 2 получает блокировку для объекта Integer при выполнении method2 () оба будут ожидать друг друга, чтобы снять блокировку на Integer и String для продолжения, что никогда не произойдет.

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

избежать тупика

Вы можете видеть, что поток 1 хочет блокировки объекта 2, который удерживается потоком 2, а поток 2 хочет блокировки объекта 1, который удерживается потоком 1. Поскольку ни один поток не желает сдаваться, существует тупик, и Программа на Java застряла.

Как избежать тупика в Java?

Теперь интервьюер подходит к заключительной части, одной из самых важных на мой взгляд;
Как исправить тупик в коде? или как избежать тупика в Java?

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class DeadLockFixed {
 
    /**
     * Both method are now requesting lock in same order, first Integer and then String.
     * You could have also done reverse e.g. first String and then Integer,
     * both will solve the problem, as long as both method are requesting lock
     * in consistent order.
     */
    public void method1() {
        synchronized (Integer.class) {
            System.out.println("Aquired lock on Integer.class object");
 
            synchronized (String.class) {
                System.out.println("Aquired lock on String.class object");
            }
        }
    }
 
    public void method2() {
        synchronized (Integer.class) {
            System.out.println("Aquired lock on Integer.class object");
 
            synchronized (String.class) {
                System.out.println("Aquired lock on String.class object");
            }
        }
    }
}

Теперь не было бы никакой тупиковой ситуации, поскольку оба метода получают доступ к блокировке литерала класса Integer и String в одном и том же порядке. Таким образом, если поток A получает блокировку для объекта Integer, поток B не будет продолжаться до тех пор, пока поток A не снимет блокировку Integer, точно так же поток A не будет заблокирован, даже если поток B удерживает блокировку строки, поскольку теперь поток B не будет ожидать, что поток A освободит Целочисленная блокировка для продолжения.

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

Смотрите оригинальную статью здесь: Как избежать тупика в Java Threads?

Мнения, высказанные участниками Java Code Geeks, являются их собственными.