【问题标题】:why java ReentrantLock not throw InterruptedException?为什么java ReentrantLock 不抛出InterruptedException?
【发布时间】:2016-05-06 08:29:37
【问题描述】:

我想在 Java 线程并发中创建竞争条件并创建死锁。 我使用 ReentrantLock,但它不会抛出 InterruptedException。

现在死锁了,我用了lockInterruptibly,但它没有抛出InterruptedException,任何人都可以告诉我为什么吗?

  public class Test {

    public static void main(String[] args) throws InterruptedException {

        final Object o1 = new Object();
        final Object o2 = new Object();

        final ReentrantLock l1 = new ReentrantLock();
        final ReentrantLock l2 = new ReentrantLock();

        Thread t1 = new Thread() {
            public void run() {
                try {
                    l1.lockInterruptibly();
                    System.out.println("I am in t1 step 1 " + o1.toString());
                    Thread.sleep(1000);
                    l2.lock();
                    try {
                        System.out.println("I am in t1 step 2 " + o2.toString());
                    } finally {
                        l2.unlock();
                    }

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Thread t2 = new Thread() {
            public void run() {
                try {
                    l2.lockInterruptibly();
                    System.out.println("I am in t2 step 1 " + o2.toString());
                    Thread.sleep(1000);
                    l1.lock();
                    try {
                        System.out.println("I am in t2 step 2 " + o1.toString());
                    } finally {
                        l1.unlock();
                    }
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        };

        t1.start();
        t2.start();
        Thread.sleep(2000);
        t1.interrupt();
        t2.interrupt();
        t1.join();
        t2.join();

    }
}

【问题讨论】:

  • 我认为competition 应该是race condition...

标签: java multithreading reentrantlock


【解决方案1】:

您的问题是每个线程都试图获取两个锁。

                // Thread 1.
                l1.lockInterruptibly();
                // ....
                l2.lock();


                // Thread 2.
                l2.lockInterruptibly();
                // ....
                l1.lock();

所以每个线程都获取一个锁,然后尝试获取另一个线程已经持有的锁。这称为死锁。

您没有看到java.lang.InterruptedException,因为线程正在等待的锁(第二个)不是可中断锁。

解决这个问题:

                // Thread 1.
                l1.lockInterruptibly();
                // ....
                l2.lockInterruptibly();


                // Thread 2.
                l2.lockInterruptibly();
                // ....
                l1.lockInterruptibly();

【讨论】:

  • 谢谢 :-),你说得对,我必须在为每个线程使用第二个锁时使用 lockInterruptibly。
【解决方案2】:

2 个线程在行中死锁:l1.lock()l2.lock()。所以当你打断他们时,他们没有反应。如果你用lockInterruptibly() 替换所有lock() 调用,你会得到异常。

编辑: 我准备了更简单的示例,可以根据需要生成竞争条件:

public class Test {

    public static void main(String[] args) throws InterruptedException {

        final ReentrantLock l1 = new ReentrantLock();
        final Random rn = new Random();

        Thread t1 = new Thread() {
            public void run() {
                try {
                    Thread.sleep(rn.nextInt(1000)); //wait 0-999 ms
                    l1.lockInterruptibly();
                    System.out.println("Thread 1 won");
                } catch (InterruptedException e) {
                    System.out.println("Thread 1 interrupted");
                }
            }
        };

        Thread t2 = new Thread() {
            public void run() {
                try {
                    Thread.sleep(rn.nextInt(1000)); //wait 0-999 ms
                    l1.lockInterruptibly();
                    System.out.println("Thread 2 won");
                } catch (InterruptedException e1) {
                    System.out.println("Thread 2 interrupted");
                }
            }
        };

        t1.start();
        t2.start();
        Thread.sleep(2000);
        t1.interrupt();
        t2.interrupt();
        t1.join();
        t2.join();

    }
}

使用此代码,您每次运行时都会随机获得两个输出之一:

Thread 1 won
Thread 2 interrupted

Thread 2 won
Thread 1 interrupted

取决于生成的随机数。

【讨论】:

    【解决方案3】:

    可重入意味着已经持有锁的单个线程可以重新获取它。

    在您的情况下,创建了两个不同的线程。

    这称为重入锁定,是一种类似于死锁和嵌套监视器锁定的情况。

    你可以在 Thread t1 中解决这个问题

                l1.lockInterruptibly();
                System.out.println("I am in t1 step 1 " + o1.toString());
                Thread.sleep(1000);
                l2.lockInterruptibly();
    

    在线程 t2 中

                l2.lockInterruptibly();
                System.out.println("I am in t2 step 1 " + o2.toString());
                Thread.sleep(1000);
                l1.lockInterruptibly();
    

    【讨论】:

      【解决方案4】:

      它不会抛出 InterruptedException,任何人都可以告诉我为什么吗?

      可能是因为创建ReentrantLock 的目的是互斥

      只要程序中的线程持有互斥锁的时间超过几分之一秒,这就是不好的代码气味。

      使lock.lock() 调用可中断会使所有使用它的程序复杂化:在每个要锁定锁的地方,都必须为InterruptedException 编写处理程序。

      我的猜测(仅此而已)是作者认为他们不应该让所有程序员都必须编写那些额外的中断处理程序,以便一些程序员可以在糟糕的设计中使用ReentrantLock .

      【讨论】: