Java并发编程(5)——饥饿与公平

前言

上一篇中我们说的主要问题是死锁发生的条件,以及应对死锁的一些解决方法,它们分别:加锁顺序、加锁时限以及死锁检测,其中死锁检测是最优的解决方法,它通过检查与目标锁相关的数据结构,查询在整个锁的关系图中,是否有某个线程正在等待当前线程持有的锁,以判断死锁是否发生。

1. 饥饿

1.1 定义

所有CPU时间片被其它线程占据导致其中一个线程无法分配到CPU时间片的状态被称为饥饿(线程可能因为饥饿致死)。

1.2 饥饿状态的原因

  • 高优先级的线程吞噬了所有低优先级线程的CPU时间;
  • 线程被永久阻塞在一个等待进入同步块的状态;
  • 线程在等待一个自身处于永久等待完成的对象(比如调用这个对象的wait()方法)。

1.2.1 线程优先级导致的饥饿

Java的线程类允许我们为每个线程对象设置优先级, public final void setPriority(int newPriority),优先级的范围是1-10,不过我们需要认识到:优先级并不保证我们的程序的执行逻辑,优先级表示的行为的准确解释依赖于程序的运行平台(我暂且理解为操作系统),因此,如果没有必要,我们最好不要改动优先级

1.2.2 永久阻塞

Java的同步代码区对于哪个线程能够允许被进入的次序并没有任何保证,这就意味着存在着某种风险即某个线程永远处在阻塞状态,因为其它线程总是先于它进入同步区。

1.2.3 等待着等待

另一种线程处于饥饿状态的原因就是,线程在等待着永远处于等待状态的对象。如果多个线程持有了某个处于wait()状态的对象,调用该对象的notify()方法并不能保证某个线程被唤醒,任何线程都可能继续保持在等待状态。因此存在这样的风险:某个线程从未被唤醒,因为其它线程总是能够被唤醒。

2. 公平

2.1 定义

这是解决饥饿问题的对策,保证所有线程都能够公平获得CPU运行时间的策略。

2.2 公平性解决办法

  • 使用锁,而不是同步块;
  • 公平锁;

2.2.1 使用锁代替同步块

2.3 注意性能开销

猜你喜欢

转载自blog.csdn.net/Ascend2015/article/details/80500633