Как избежать dead lock. Способ 1. Приоритеты ресурсов

Как избежать dead lock. Способ 1. Приоритеты ресурсов

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

Для предыдущего примера будем определять приоритет ресурса по его имени: у кого имя "больше", тот и будет захватываться первым. Для этого класс ITishnik будет имплементировать интерфейс Comparable<T>


abstract class ITishnik implements Comparable<ITishnik> {
    String name;

    ITishnik(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public int compareTo(ITishnik o) {
        String nameIt1 = getName();
        String nameIt2 = o.getName();
        int comparing = nameIt1.compareTo(nameIt2);
        return comparing;
    }
}
В класс Application добавим метод, который будет определять приоритет ресурса. Для удобства он будет возвращать очередь ресурсов.


abstract class Application extends Thread {

    Developer developer;
    Tester tester;

    public Application(Developer developer, Tester tester) {
        this.developer = developer;
        this.tester = tester;
    }

    protected ArrayDeque<ITishnik> sortResources(ITishnik it1, ITishnik it2) {
        // сортируем ресурсы
        Set<ITishnik> set = new TreeSet<>();
        set.add(it1);
        set.add(it2);
        return new ArrayDeque<>(set);
    }

    protected void sleepThread(String lockname) {
        try {
            System.out.println(this.getName() + " acqured " + lockname);
            Thread.sleep((long) Math.random() * 1000L);
        } catch (InterruptedException e) {
            e.printStackTrace(System.out);
        }
    }
}
Изменим метод run() для приложений, добавив захват ресурсов в соответствии с приоритетом.


class AndroidApp extends Application {

    public AndroidApp(Developer developer, Tester tester) {
        super(developer, tester);
        this.setName("AndroidApp");
    }

    @Override
    public void run() {
        ArrayDeque<ITishnik> queue = sortResources(developer, tester);
        ITishnik firstLockObject = queue.getFirst();
        ITishnik seckondLockObject = queue.getFirst();
        synchronized (firstLockObject) {
            sleepThread(firstLockObject.getName());
            synchronized (seckondLockObject) {
                tester.testApp();
                developer.fixBugs();
            }
        }
    }
}


class iPhoneApp extends Application {

    public iPhoneApp(Developer developer, Tester tester) {
        super(developer, tester);
        this.setName("iPhoneApp");
    }

    @Override
    public void run() {
        ArrayDeque<ITishnik> queue = sortResources(developer, tester);
        ITishnik firstLockObject = queue.getFirst();
        ITishnik seckondLockObject = queue.getFirst();
        synchronized (firstLockObject) {
            sleepThread(firstLockObject.getName());
            synchronized (seckondLockObject) {
                developer.code();
                tester.testApp();
            }
        }
    }
}
Программист и тестировщик останутся без изменений


class Developer extends ITishnik {

    Developer(String name) {
        super(name);
    }

    void code() {
        System.out.println("Developer is coding...");
    }

    void fixBugs() {
        System.out.println("Developer is fixing bugs...");
    }
}


class Tester extends ITishnik {

    Tester(String name) {
        super(name);
    }

    void testApp() {
        System.out.println("Tester is testing...");
    }
}


public static void main(String[] args) {
        Developer developer = new Developer("Developer");
        Tester tester = new Tester("Tester");

        AndroidApp android = new AndroidApp(developer, tester);
        iPhoneApp iPhone = new iPhoneApp(developer, tester);

        android.start();
        iPhone.start();
}
Вывод будет такой:


/*
iPhoneApp acqured Developer
Developer is coding...
Tester is testing...
AndroidApp acqured Developer
Tester is testing...
Developer is fixing bugs...
*/
Или такой:


/*
AndroidApp acqured Developer
Tester is testing...
Developer is fixing bugs...
iPhoneApp acqured Developer
Developer is coding...
Tester is testing...
*/
Многопоточность