在java中使用单个线程的死锁

时间:2013-12-07 05:53:04

标签: java multithreading deadlock

我读到单个用户线程可以使用系统线程死锁。 我的问题是,这个系统线程可以是与java线程共享资源的任何线程(不一定是java线程)。例如。 :锁定文件后I / O在2个文件上。 因此,除非系统线程与java线程共享资源,否则它无法创建死锁。 是否有上述声明中的任何其他示例以粗体显示。

另一个问题:

如果有2个函数使用2个锁,它们应该以相同的顺序锁定。但是必须以相同的相反顺序发布。两种功能的锁定释放顺序是否不同 例如:

function1() {
   try {
    lock1.lock();
    lock2.lock();
   } finally {
     lock2.unlock();
     lock1.unlock();
   }
}

function2() {
   try {
    lock1.lock();
    lock2.lock();
   } finally {
     lock1.unlock();
     lock2.unlock();
   }
}

参考链接:if a single user thread deadlocks, a system thread must also be involved

2 个答案:

答案 0 :(得分:3)

如果仅涉及Java对象监视器锁

,则单个Java线程无法对其自身死锁是正确的。

“系统线程”的含义并不完全清楚。即使在运行简单程序时,JVM也会运行多个线程,例如终结器线程,或者用于GUI应用程序,即事件分发线程(EDT)。这些线程可能会占用Java对象监视器锁,因此会对单个应用程序线程造成死锁。

单个Java线程可以对外部进程死锁,而不是其他Java线程。例如,请考虑以下程序:

public static void main(String[] args) throws Exception {
    Process proc = Runtime.getRuntime().exec("cat");

    byte[] buffer = new byte[100_000];
    OutputStream out = proc.getOutputStream();
    out.write(buffer);
    out.close();

    InputStream in = proc.getInputStream();
    int count = in.read(buffer);
    System.out.println(count);
}

这会运行“cat”,它只是从stdin复制到stdout。该程序通常会死锁,因为它会将大量数据写入子进程。子进程将阻止写入其输出,因为父进程尚未读取它。这可以防止子进程读取其所有输入。因此,Java线程已经对子进程陷入僵局。 (处理这种情况的常用方法是让另一个Java线程读取子进程输出。)

如果单个Java线程正在等待从未发生的通知,则它可能会死锁。考虑:

public static void main(String[] args) throws InterruptedException {
    Object obj = new Object();
    synchronized (obj) {
        obj.wait();
    }
}

此程序永远不会终止,因为没有任何东西会通知obj或中断线程。这似乎有点人为,但这种“失败的唤醒问题”的实例确实在实践中发生。有错误的系统可能无法正确设置状态,或者在错误的时间拨打notify,或拨打notify而不是notifyAll,在wait电话中阻止线程等待永远不会发生的通知。在这种情况下,可能很难识别该线程死锁的另一个线程,因为该线程可能在过去已经死亡,或者它可能尚未创建。但它肯定是僵局。

<强>更新

我遇到了另一个单线程死锁的例子。 Goetz等。 al。, Java Concurrency In Practice p。 215,描述线程饥饿死锁。考虑一个

的例子
  

提交任务并等待其结果的任务在单线程Executor中执行。在这种情况下,第一个任务将永远等待,永久停止该任务,所有其他任务等待在Executor执行。

(单线程Executor基本上是一个处理任务队列的线程,一次一个。)

更新2

我在文献中找到了另一个单线程死锁的例子:

  

使用监视器可以发生三种成对死锁模式。当然,在实践中,死锁通常涉及两个以上的过程,在这种情况下,观察到的实际模式往往更复杂;相反,单个进程也可能与自身发生死锁(例如,如果一个入口过程是递归的)。

兰普森,巴特勒W.和大卫D.雷德尔。 在Mesa中使用过程和监视器的经验。 CACM Vol。 1980年2月23日第2期。

请注意,在本文中,“进程”指的是我们称之为线程的内容,而“入口过程”就像是同步方法。但是,在Mesa中,监视器不可重入,因此如果单个线程第二次尝试进入同一监视器,则单个线程可能会死锁。

Posix线程也是如此。如果一个线程在正常(即非递归)互斥锁上第二次调用pthread_mutex_lock,则该线程将自行死锁。

从这些例子中,我得出结论,“死锁”并不严格要求两个或多个线程。

答案 1 :(得分:1)

对于第一个问题:想想任何Swing应用程序。例如,主线程可能容易干扰事件调度线程(因为所有事件处理都发生在该特定线程中)。此外,您可以使用终结器线程。

对于第二个问题:是的,你可以按任何顺序释放锁。