Повторная блокировка и взаимоблокировка с Java

может кто-нибудь объяснить мне, как Reentrant lock и deadlock связаны друг с другом с примером кода Java (псевдо)?

4 ответов


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

public synchronized void functionOne() {

    // do something

    functionTwo();

    // do something else

    // redundant, but permitted...
    synchronized(this) {
        // do more stuff
    }    
}

public synchronized void functionTwo() {
     // do even more stuff!
}

в non-reentrant блокировки, у вас будет тупиковая ситуация, когда вы пытаетесь вызвать functionTwo() С functionOne() потому что поток должен был бы ждать блокировки...который он держит сам.

Deadlock, конечно, является злой ситуацией, в которой поток 1 держит блокировку A и ждет блокировки B в то время как поток 2 удерживает блокировку B и ждет блокировки A. Таким образом, ни один не может продолжаться. Этот пример кода создает затор:

public synchronized void deadlock() throws InterruptedException {
    Thread th = new Thread() {
        public void run() {
            deadlock();
        }
    }.start();

    th.join();
}

вызывающий поток пытается дождаться порожденного потока, который, в свою очередь, не может вызвать deadlock() пока вызывающий не выйдет. Ка-бум!


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

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

ReentrantLock lock1 = new ReentrantLock();
ReentrantLock lock2 = new ReentrantLock();

public void methodA() {
    lock1.lock();
    lock2.lock();
    // do something and un lock both.
}

public void methodB() {
    lock2.lock();
    lock1.lock();
    // do something and un lock both.
}

как вы можете видеть, поток может вызвать methodA и получить lock1, ожидающий lock2, а другой поток-вызвать methodB и получить lock2, ожидающий lock1.


тем не менее, это возможно для потока, чтобы заблокировать себя. Образец является ReentrantReadWriteLock, поскольку он не поддерживает обновление блокировки чтения для блокировки записи.

ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
rwl.readLock().lock();
// do we need to update?
rwl.writeLock().lock(); // will wait for the readLock() to be released!

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

class A {
     private static int VALUE;
     static {
        Thread t = new Thread() {
            public void run() {
                // waits for the A class to load.
                VALUE = someLongTask();
            }
        };
        t.start();
        // waits for the thread.
        t.join();
    }
}

опять тупик!


вот пример взаимоблокировки с ReentrantLock

class Deadlock {
    private static final ReentrantLock l1 = new ReentrantLock();

    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            public void run() {
                System.out.println("A Trying to lock...");
                l1.lock();
                System.out.println("A Locked...");
                try {
                    Thread t = new Thread(new Runnable() {
                        public void run() {
                            System.out.println("B Trying to lock...");
                            l1.lock();
                            System.out.println("B Must not print");
                            try {
                            } finally {
                                System.out.println("B Trying to unlock...");
                                l1.unlock();
                                System.out.println("B Unlocked...");
                            }
                        }
                    });
                    t.start();
                    try {
                        t.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } finally {
                    System.out.println("A Trying to unlock...");
                    l1.unlock();
                    System.out.println("A Unlocked...");
                }
            }
        });
        t.start();
    }
}

чтобы разрешить тупик, прокомментируйте вызов t.join, вместе с заключением try / catch.


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

Что касается взаимоблокировки, если вы вызываете защищенный блок кода из защищенного блока кода, вам понадобится реентрантная блокировка (или вы будете в тупике, ожидая себя).