基本线程同步(七)修改Lock的公平性

修改Lock的公平性

在ReentrantLock类和 ReentrantReadWriteLock类的构造器中,允许一个名为fair的boolean类型参数,它允许你来控制这些类的行为。默认值为 false,这将启用非公平模式。在这个模式中,当有多个线程正在等待一把锁(ReentrantLock或者 ReentrantReadWriteLock),这个锁必须选择它们中间的一个来获得进入临界区,选择任意一个是没有任何标准的。true值将开启公平 模式。在这个模式中,当有多个线程正在等待一把锁(ReentrantLock或者ReentrantReadWriteLock),这个锁必须选择它们 中间的一个来获得进入临界区,它将选择等待时间最长的线程。考虑到之前解释的行为只是使用lock()和unlock()方法。由于tryLock()方 法并不会使线程进入睡眠,即使Lock接口正在被使用,这个公平属性并不会影响它的功能。

在这个指南中,我们将修改使用Lock同步代码块食谱示例来使用这个属性,并且观察公平与非公平模式之间的差别。

准备工作…

我们将要修改使用Lock同步代码块食谱的示例,所以阅读那个食谱来实现这个示例。

如何做…

按以下步骤来实现的这个例子:

1.实现有使用Lock同步代码块食谱中解释的示例。

2.在PrintQueue类,修改Lock对象的构造,如下:


1 private Lock queueLock=new ReentrantLock(true);

3.修改printJob()方法,使用两个代码块分离打印的模拟,在它们之间释放锁。


01 public void printJob(Object document){
02 queueLock.lock();
03 try {
04 Long duration=(long)(Math.random()*10000);
05 System.out.println(Thread.currentThread().getName()+":
06 PrintQueue: Printing a Job during "+(duration/1000)+" seconds");
07 Thread.sleep(duration);
08 } catch (InterruptedException e) {
09 e.printStackTrace();
10 } finally {
11 queueLock.unlock();
12 }
13 queueLock.lock();
14 try {
15 Long duration=(long)(Math.random()*10000);
16 System.out.println(Thread.currentThread().getName()+":
17 PrintQueue: Printing a Job during "+(duration/1000)+" seconds");
18 Thread.sleep(duration);
19 } catch (InterruptedException e) {
20 e.printStackTrace();
21 } finally {
22 queueLock.unlock();
23 }
24 }

4.修改Main类中,启动线程的代码块。新的代码块如下:


1 for (int i=0; i<10; i++){
2 thread[i].start();
3 try {
4 Thread.sleep(100);
5 } catch (InterruptedException e) {
6 e.printStackTrace();
7 }
8 }

它是如何工作的…

在以下截图中,你可以看到执行这个例子的一个部分输出:

基本线程同步(七)修改Lock的公平性

所有线程都创建一个0.1秒的差异,第一需要获取锁的控制权的线程是Thread0,然后是Thread1,以此类推。当Thread0正在运行第一个由锁 保护的代码块时,有9个线程正在那个代码块上等待执行。当Thread0释放锁,它需要马上再次获取锁,所以我们有10个线程试图获取这个锁。当启用代码 模式,Lock接口将会选择Thread1,它是在这个锁上等待最长时间的线程。然后,选择Thread2,然后是Thread3,以此类推。直到所有线 程都通过了这个锁保护的第一个代码块,否则,没有一个线程能执行该锁保护的第二个代码块。

一旦所有线程已经执行完由这个锁保护的第一个代码块,再次轮到Thread0。然后,轮到Thread1,以此类推。

为了看与非公平模式的差异,改变传入锁构造器的参数,传入false值。在以下截图中,你可以看到修改示例后的执行结果:

基本线程同步(七)修改Lock的公平性

在这种情况下,线程按被创建的顺序执行,但每个线程各自执行两个受保护的代码块。然而,这种行为的原因是没有保证的,正如之前解释的,这个锁将选择任意一个线程获得访问保护代码块。在这种情况下,JVM不能保证线程的执行顺序。

不止这些…

读/写锁在它们的构造器中也有公平参数。这个参数在这种锁中的行为与本指南的解释是一样的。

参见

  • 在第2章,基本线程同步中使用Lock同步代码块的指南
  • 在第2章,基本线程同步中使用读/写锁同步数据访问的指南
  • 在第7章,制订并发类中实现一个自定义的Lock类的指南