在介绍简单自旋锁的时候,缺点中说简单自旋锁不能保证公平性,就会产生线程饥饿问题。那么到底什么是线程饥饿问题,什么又是公平性?
线程饥饿
如果一个线程的CPU执行时间片全部被其他线程抢走,而得不到CPU执行权,这个线程的状态就被称为饥饿。
线程饥饿原因
导致线程饥饿的原因:
- 高优先级线程吞噬低优先级线程的CPU时间片
- 线程一直等待获得锁而一直处于阻塞状态
- 处于等待状态的线程,永远无法唤醒
1.对于高优先级线程吞噬低优先级线程的CPU时间片没什么好说的,就是高优先级线程把CPU执行时间片全占用了
2.对于线程一直等待获得锁而一直处于阻塞状态这种情况很好立即,就是线程在竞争锁的时候,不能保证获得锁的顺序,导致获得锁的一直是其他线程,本线程可能永远获得不到锁,一直处于阻塞状态
3.对于处于等待状态的线程,永远无法唤醒这种情况说明一下:
对于处于等待状态的线程,在等待其他线程唤醒,这个其他线程有可能也处于等待状态,而且这个其他线程处于永久等待状态,那么对于一开始那个线程来说就是饥饿状态,一直得不到执行机会。
对于上面那个例子,还有可能就是其他线程在等待一开始那个线程,形成一种相互等待的情况,那么谁都没有CPU执行权限,导致两个线程都饿死。还有种情况就是其他线程在唤醒的时候执行的notify()方法,但是notify()方法不保证被唤醒的顺序,任何线程都有可能被唤醒,所以理论上一开始那个线程永远都得不到唤醒,唤醒的总是其他线程。
线程饥饿解决
1.对于优先级问题解决很简单,就是不要改变线程优先级的值
2.对于阻塞问题,就是使用公平性的锁,公平性保证先来竞争的锁先获得锁,后来的竞争锁的线程后获得锁,具体实现可以使用队列方式,就没有因为不公平导致多个线程竞争导致某个线程一直没有获得机会,因为是公平的,所以每个线程都有机会获得到锁执行同步代码
所以这就是公平性,顾名思义,先来竞争锁的线程先获得锁,后来竞争锁的线程后获得锁,每个线程都是公平的。
3.对于线程等待状态下,一直没有唤醒机会的问题,我们只需要在唤醒的时候执行唤醒哪个线程就可以了,不使用notify()方法,这种随机的方法同样不能保证“公平性”,可以使用JUC包中工具类解决此类问题。(至于JUC包中的工具,以后会专门开文章做研究)
公平锁
对于公平锁有很多的实现,在这里不做说明,以后在研究锁的时候会再说明,在这里只说明饥饿与公平的问题。
总结
所以对于简单自旋来说是不公平的,会有线程饥饿问题,所以引入了TicketLock锁,解决公平性问题