8

有人可以用 Java 代码(伪)示例向我解释如何Reentrant lock相互关联吗?deadlock

4

4 回答 4

14

可重入锁定机制允许持有锁的线程重新进入临界区。这意味着您可以执行以下操作:

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!
}

在不可重入锁中,当您尝试调用时,您会遇到死锁情况functionTwo()functionOne()因为线程必须等待锁......它自己持有。

当然,死锁是线程 1 持有锁 A 并等待锁 B 而线程 2 持有锁 B 并等待锁 A 的邪恶情况。因此,两者都无法继续。此代码示例创建了一个死锁:

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

    th.join();
}

调用线程尝试等待生成的线程,而在调用deadlock()者退出之前,该线程无法调用。咔嚓!

于 2011-06-24T16:40:34.030 回答
6

当线程等待一个永远不会成为真的条件时,就会发生死锁。

明显的情况是当您尝试锁定两个锁时,不同线程以不同的顺序锁定。

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

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

public void methodB() {
    lock2.lock();
    lock1.lock();
    // do something and unlock 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();
    }
}

再次陷入僵局!

于 2011-06-24T16:53:53.257 回答
1

这是一个使用 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。

于 2011-06-24T16:51:54.340 回答
0

可重入锁将允许锁持有者进入代码块,即使它已经通过输入其他代码块获得了锁。一个不可重入锁将拥有锁持有者块,因为它必须释放从另一个代码块获得的锁以重新获得相同的锁才能进入需要代码块的嵌套锁。

就死锁而言,如果您从受保护的代码块中调用受保护的代码块,您将需要一个可重入锁(或者您在等待自己时会死锁)。

于 2011-06-24T16:41:43.820 回答