监控锁接口

锁接口是 Java Concurrency API 提供以获得一个代码块的同步的基本机制之一。 它可以定义一个临界区。临界区是一个访问共享资源的代码块,同一时间不能被多于一个线程执行。 此机制由 Lock 接口和 ReentrantLock 类实现。

在本节中,将学习能获得关于锁对象的什么信息以及如何获得那些信息。

实现

本节的示例代码在 com.elanzone.books.noteeg.chpt8.sect02 package中

  • MyLock 类 : extends ReentrantLock

    • getOwnerName() 方法 : 使用 Lock 类的 protected 方法 getOwner() 返回拥有对锁的控制的线程名称
            public String getOwnerName() {
                if (this.getOwner() == null) {
                    return "None";
                }
    
                return getOwner().getName();
            }
    
    • getThreads() 方法 : 使用 Lock 类的 protected 方法 getQueuedThreads() 返回等待锁的线程列表
            public Collection<Thread> getThreads() {
                return this.getQueuedThreads();
            }
    
  • Runnable 实现类 : Task : implements Runnable

    • 属性 lock : Lock 对象 (在使用时将被设置为 MyLock 实例)
    • 构造函数初始化属性为参数值
            private Lock lock;
    
            public Task(Lock lock) {
                this.lock = lock;
            }
    
    • run() 方法:
      循环5次,每次:
      1. 使用 lock() 方法获得锁
      2. 输出当前线程名称后睡上 500 毫秒
      3. 最后使用 unlock() 方法释放锁
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    lock.lock();
                    System.out.printf("%s: Get the Lock.\n", Thread.currentThread().getName());
    
                    try {
                        TimeUnit.MILLISECONDS.sleep(500);
                        System.out.printf("%s: Free the Lock.\n", Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            }
    
  • 控制类:Main

    1. 创建一个 MyLock 对象
    2. 使用 MyLock 对象创建 5 个 Task 对象及对应的线程,并启动线程
    3. 循环 15 次,每次:
      1. 调用 MyLock 的 getOwner() 方法获得并输出锁的拥有者信息
      2. 调用锁的 hasQueuedThreads() 方法判断是否有线程在等待锁
        如果有线程在等锁,则:
        1. 调用锁的 getQueueLength() 方法获得等锁的线程个数
        2. 调用 MyLock 的 getThreads() 方法及 Thread 的 getName() 方法获得等锁中的线程名称等信息
      3. 调用锁的 isFair() 和 isLocked() 方法分别得知锁是否公平、是否被某个线程占有
      4. 睡上1秒
            public static void main(String[] args) throws Exception {
                MyLock lock = new MyLock();
                Thread threads[] = new Thread[5];
                for (int i = 0; i < 5; i++) {
                    Task task = new Task(lock);
                    threads[i] = new Thread(task);
                    threads[i].start();
                }
    
                for (int i = 0; i < 15; i++) {
                    System.out.printf("Main: Logging the Lock\n");
                    System.out.printf("************************\n");
                    System.out.printf("Lock: Owner : %s\n", lock.getOwnerName());
    
                    System.out.printf("Lock: Queued Threads: %s\n", lock.hasQueuedThreads());
                    if (lock.hasQueuedThreads()) {
                        System.out.printf("Lock: Queue Length: %d\n", lock.getQueueLength());
                        System.out.printf("Lock: Queued Threads: ");
                        Collection<Thread> lockedThreads = lock.getThreads();
                        for (Thread lockedThread : lockedThreads) {
                            System.out.printf("%s ", lockedThread.getName());
                        }
                        System.out.printf("\n");
                    }
    
                    System.out.printf("Lock: Fairness: %s\n", lock.isFair());
                    System.out.printf("Lock: Locked: %s\n", lock.isLocked());
                    System.out.printf("************************\n");
    
                    TimeUnit.SECONDS.sleep(1);
                }
            }
    

讲解

在本节中,MyLock 类扩展 ReentrantLock 类来返回信息。不扩展的话返回不了,因为数据是 ReentrantLock 类的 protected 数据。 由 MyLock 类实现的方法有:

  • getOwnerName(): 只有一个线程能执行一个由一个锁对象保护的临界区。
    锁保存正执行临界区的线程。此线程由 ReentrantLock 类的 protected getOwner() 方法返回。
    getOwnerName() 方法使用 getOwner() 方法以获得线程的名字。
  • getThreads(): 当一个线程正在执行一临界区,其他尝试进入临界区的线程被催眠直到能继续执行该临界区。
    ReentrantLock 类的 protected 方法 getQueuedThreads() 返回正在等待执行临界区的线程列表。
    getThreads() 方法返回由 getQueuedThreads() 方法返回的结果。

我们也用了其他的在 ReentrantLock 类中实现的一些方法:

  • hasQueuedThreads(): 返回一个 Boolean 值表示是否有线程在等待获得锁
  • getQueueLength(): 返回正在等待获得锁的线程数量
  • isLocked(): 返回一个 Boolean 值表示此锁是否被一个线程占有
  • isFair(): 返回一个 Boolean 值表示此锁是否激活了公平模式

了解更多

ReentrantLock 类中有其他的一些方法能被用于获得关于一个锁对象的信息:

  • getHoldCount(): 返回当前线程获得锁的次数
  • isHeldByCurrentThread(): 返回一个 Boolean 值表示锁是否被当前线程持有