多线程活跃性,性能与测试.ppt
文本预览下载声明
1.死锁 当一个线程永远的持有一个锁,并且其他线程都尝试获得这个锁时,那么他们将永远被阻塞。在线程A持有锁L并想获得锁M的同时,线程B持有锁M并尝试获得锁L,那么这两个线程将永远的等待下去。这种情况就是最简单的死锁形式。 1.1锁顺序死锁 LeftRightDeadlock存在死锁风险.leftRight和rightLeft这两个方法分别获得left锁和right锁。如果一个线程调用了leftRight,而另一个线程调用了rightLeft,并且这两个线程的操作是交错执行,如图,那么他们会发生死锁。 例子: 1.2动态的锁顺序死锁 A:transferMoney(myAccount,yourAccount,10); B:transferMoney(yourAccount,myAccount,20); 在上图中我们使用System.identityHashCode方法返回object.hashcode返回值来定义锁的顺序,消除了发生死锁的可能。 1.3会发生死锁循环 2.其他活跃性危险 尽管死锁是最常见的活跃性危险,但在并发程序中还存在一些其他的活跃性危险,包括:饥饿、丢失信号和活锁等。 2.1 饥饿 当线程由于无法访问它所有需要的资源而不能继续执行时,就发生了“饥饿”。引发饥饿的最常见资源就是cpu时钟周期。 在一个线程池中,如果一个任务依赖于其他任务的执行,就可能产生死锁。对应一个单线程话的Executor,一个任务将另一个任务提交到相同的Executor中,并等待新提交的任务的结果,这总会引发死锁。第二个任务滞留在工作队列中,直到第一个任务完成,但是第一个任务不会完成,因为它在等待第二个任务的完成。 同样在一个大的线程池中,如果所有线程执行的任务都阻塞在线程池中,等待着仍然处于同一个工作队列中的其他任务,那么会发生同样的问题。这就是线程饥饿死锁。如下图代码: 2.2 活锁 活锁(Livelock)是另一种形式的活跃性问题,该问题尽管不会阻塞线程,但也不能继续执行,因为线程将不断重复执行相同操作,而且总会失败。活锁通常发生在处理事务消息的应用程序中:如果不能成功地处理某个消息,那么消息处理机制将回滚整个事务,并将它重新放到队列的开头。 小结: 活跃性故障时一个非常严重的问题,以为当出现活跃性故障时,除了中止应用程序外没有其他任何机制可以帮助从这种故障时恢复过来。最常见的活跃性故障就是锁顺序死锁。在设计时应该避免产生锁顺序死锁:确保线程在获取多个锁时采用一致的顺序。最好的解决方法是在程序中始终使用开放调用。这将大大减少需要同时持有多个锁的地方,也更容易发现这些地方。
显示全部