java 修改锁的公平性

是什么?为什么?怎么做?

 

一、是什么

加锁后,线程得不到资源阻塞,放入阻塞队列,当资源存在的时候,怎么选择线程进行唤醒?如何进行唤醒决定了线程是公平还是不公平。

在ReentrantLock类和ReentrantReadWriteLock类的构造器中,允许一个名为fair的boolean类型参数,它允许你来控制唤醒线程的行为。

默认值为false,这将启用非公平模式。在这个模式中,当有多个线程正在等待一把锁(ReentrantLock或者ReentrantReadWriteLock),这个锁必须选择它们中间的一个来获得进入临界区,选择任意一个是没有任何标准的(随机选)。

true值将开启公平模式。在这个模式中,当有多个线程正在等待一把锁(ReentrantLock或者ReentrantReadWriteLock),这个锁必须选择它们中等待时间最长的一个来获得进入临界区(选等待时间最长的)。

考虑到之前解释的行为只是使用lock()和unlock()方法(lock和unlock是迟早一定会获得锁的情况了,只是时间问题而已,所以适用公平模式)。由于tryLock()方法并不会使线程进入睡眠(tryLock方法只是尝试获取锁而已,能不能获得是未知的,如果没获取,可能去干些别的事,所以不适用公平模式),即使Lock接口正在被使用,这个公平属性并不会影响它的功能。

二、为什么

为什么使用公平锁,涉及的是饥饿和公平问题(涉及一些操作系统的思想)。java线程中的饥饿问题往往体现在以下几点:

(1)高优先级线程的抢占

首先要知道,线程有两种调度模型:

1.分时调度模型

所有线程轮流使用cpu的使用权,平均分配给每个线程占用cpu的时间片。

2.抢占式调度模型

优先让给线程高的线程使用cpu,如果线程的优先级相同,那么随机选择一个,优先级高的线程获取的cpu的时间片相对多一些。

java使用的是抢占式调度模型,所以,如果你给一些线程设置了优先级,那么cpu将尽量把执行资源让给高优先级的线程(但是不代表高优先级的线程全部都先执行完,再执行低优先级的线程)。

如果总是高优先级线程抢占大部分cpu资源(相应的将抢占大量的运行时间),那么低优先级的线程可能因为无法获取足够的cpu资源而被饿死。

(2)线程永远被阻塞

java的同步代码区对线程进入的次序没有任何保障,所以某些线程存在被永久阻塞的风险,因为其他线程总是能持续地先于它获得访问。

上面的那种情况,线程至少有机会可以执行。但是在这里,线程可能永远被阻塞,没有任何执行的机会,被饿死。

(3)线程没有被唤醒

java中的notify不一定可靠,不能保证每条处于等待状态的线程都会被唤醒。如果某条线程一直没有被唤醒,那么将处于一直等待的状态,被饿死。

三、怎么做

1.非公平锁

这里设置了一个非公平锁(其实不加false也可以,就是默认情况):

运行结果为:

可以看见,在非公平的情况下,锁被释放后,因为有多条线程正在等待这个锁,所以必须选择它们中间的一个来获得进入临界区。选择任意一个是没有任何标准的,没有固定的顺序可言(在这里,线程6和7就先于5拿到了锁)。

2.公平锁

首先把false改成true。

结果为:

在公平的情况下,哪条线程等待最久,最先获得锁。所以这里体现出了顺序。

四、总结

顺便复习了一下操作系统,感觉又有了新的收获。

发表评论

电子邮件地址不会被公开。 必填项已用*标注