设想一个线程调用a() 但在获得 lock 之后在获得 lock 之前被剥夺运行权 第二个线程进入运行调用 b() 获得了 lock 但是由于第一个线程占用 lock 所以它无法获得 lock 所以它随后处于等待状态此时第一个线程被唤醒它试图获得 lock 但是由于被第二个线程占据所以无法获得此时出现死锁
编译器(或虚拟机)会重新排列请求锁的顺序使lock 总是被首先获得这就消除了死锁
但是这种方法对多线程不一定总成功所以得提供一些方法来自动打破死锁一个简单的办法就是在等待第二个锁时常释放已获得的锁
如果等待锁的每个程序使用不同的超时值就可打破死锁而其中一个线程就可运行我建议用以下的语法来取代前面的代码
synchronized语句将永远等待但是它时常会放弃已获得的锁以打破潜在的死锁可能在理想情况下每个重复等待的超时值比前一个相差一随机值
改进wait() 和 notify()
wait()/ notify() 系统也有一些问题 无法检测 wait() 是正常返回还是因超时返回 无法使用传统条件变量来实现处于一个信号(signaled)状态 太容易发生嵌套的监控(monitor)锁定
超时检测问题可以通过重新定义wait() 使它返回一个 boolean 变量 (而不是 void ) 来解决一个 true 返回值指示一个正常返回而 false 指示因超时返回
基于状态的条件变量的概念是很重要的如果此变量被设置成false 状态那么等待的线程将要被阻断直到此变量进入 true 状态任何等待 true 的条件变量的等待线程会被自动释放 (在这种情况下 wait() 调用不会发生阻断)
嵌套监控锁定问题非常麻烦我并没有简单的解决办法嵌套监控锁定是一种死锁形式当某个锁的占有线程在挂起其自身之前不释放锁时会发生这种嵌套监控封锁
此例中在get() 和 put() 操作中涉及两个锁一个在 Stack 对象上另一个在 LinkedList 对象上下面我们考虑当一个线程试图调用一个空栈的 pop() 操作时的情况此线程获得这两个锁然后调用 wait() 释放 Stack 对象上 的锁但是没有释放在 list 上的锁如果此时第二个线程试图向堆栈中压入一个对象它会在 synchronized(list) 语句上永远挂起而且永远不会被允许压入一个对象由于第一个线程等待的是一个非空栈这样就会发生死锁这就是说第一个线程永远无法从 wait() 返回因为由于它占据着锁而导致第二个线程永远无法运行到 notify() 语句
在这个例子中有很多明显的办法来解决问题例如对任何的方法都使用同步但是在真实世界中解决方法通常不是这么简单
一个可行的方法是在wait() 中按照反顺序释放当前线程获取的 所有 锁然后当等待条件满足后重新按原始获取顺序取得它们但是我能想象出利用这种方式的代码对于人们来说简直无法理解所以我认为它不是一个真正可行的方法如果您有好的方法请给我发 email
我也希望能等到下述复杂条件被实现的一天例如
其中a b 和 c 是任意对象
修改Thread 类
同时支持抢占式和协作式线程的能力在某些服务器应用程序中是基本要求尤其是在想使系统达到最高性能的情况下我认为 Java 编程语言在简化线程模型上走得太远了并且 Java 编程语言应支持 Posix/Solaris 的绿色(green)线程和轻便(lightweight)进程概念(在(Taming Java Threads 第一章中讨论) 这就是说有些 Java 虚拟机的实现(例如在 NT 上的 Java 虚拟机)应在其内部仿真协作式进程其它 Java 虚拟机应仿真抢占式线程而且向 Java 虚拟机加入这些扩展是很容易的
一个 Java 的Thread 应始终是抢占式的这就是说一个 Java 编程语言的线程应像 Solaris 的轻便进程一样工作 Runnable 接口可以用于定义一个 Solaris 式的绿色线程此线程必需能把控制权转给运行在相同轻便进程中的其它绿色线程
能有效地为Runnable 对象产生一个绿色线程并把它绑定到由 Thread 对象代表的轻便进程中这种实现对于现有代码是透明的因为它的有效性和现有的完全一样
把Runnable 对象想成为绿色线程使用这种方法只需向 Thread 的构造函数传递几个 Runnable 对象就可以扩展 Java 编程语言的现有语法以支持在一个单一轻便线程有多个绿色线程(绿色线程之间可以相互协作但是它们可被运行在其它轻便进程 ( Thread 对象) 上的绿色进程( Runnable 对象) 抢占)例如下面的代码会为每个 runnable 对象创建一个绿色线程这些绿色线程会共享由 Thread 对象代表的轻便进程
[] [] [] [] [] [] []