ReentrantLock类中的方法

ReentrantLock类中的方法

ReentrantLock类中有很多的方法:

  • getHoldCount():当前线程调用 lock() 方法的次数
  • getQueueLength():当前正在等待获取 Lock 锁的线程的估计数
  • getWaitQueueLength(Condition condition):当前正在等待状态的线程的估计数,需要传入 Condition 对象
  • hasWaiters(Condition condition):查询是否有线程正在等待与 Lock 锁有关的 Condition 条件
  • hasQueuedThread(Thread thread):查询指定的线程是否正在等待获取 Lock 锁
  • hasQueuedThreads():查询是否有线程正在等待获取此锁定
  • isFair():判断当前 Lock 锁是不是公平锁
  • isHeldByCurrentThread():查询当前线程是否保持此锁定
  • isLocked():查询此锁定是否由任意线程保持
  • tryLock():线程尝试获取锁,如果获取成功,则返回 true,如果获取失败(即锁已被其他线程获取),则返回 false
  • tryLock(long timeout,TimeUnit unit):线程如果在指定等待时间内获得了锁,就返回true,否则返回 false
  • lockInterruptibly():如果当前线程未被中断,则获取该锁定,如果已经被中断则出现异常

1.getHoldCount()方法
class Service {

    private ReentrantLock lock = new ReentrantLock();

    public void serviceMethod() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " method1 getHoldCount:"
                    + lock.getHoldCount());
            serviceMethod2();
        } finally {
            lock.unlock();
        }
    }

    public void serviceMethod2() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " method2 getHoldCount:"
                    + lock.getHoldCount());
        } finally {
            lock.unlock();
        }
    }

}

public class Run {

    public static void main(String[] args) {
        Service service = new Service();
        service.serviceMethod();
    }

}

结果是:

main method1 getHoldCount:1
main method2 getHoldCount:2

线程 main 中执行了 serviceMethod() 方法,该方法执行了一次 lock() 方法,在该方法中,调用了 serviceMethod2() 方法,该方法中再一次调用了 lock() 方法。因此线程 main 中保持锁定的个数为2,即调用 2 次 lock() 方法。同时,还能说明,ReentrantLock和synchronized一样,锁都是可重入的

2.getQueueLength()方法

该方法的作用是返回正在等待获取此锁定的线程估计数,比如有 5 个线程,1 个线程首先执行 await() 方法,那么在调用 getQueueLength() 方法后的返回值是 4,说明有 4 个线程在等待 lock 锁的释放

class Service2 {

    public ReentrantLock lock = new ReentrantLock();

    public void serviceMethod() {
        try {
            lock.lock();
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

public class Run2 {

    public static void main(String[] args) throws InterruptedException {
        Service2 service2 = new Service2();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                service2.serviceMethod();
            }
        };

        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(runnable);
            threads[i].start();
        }

        Thread.sleep(2000);
        System.out.println(Thread.currentThread().getName()
                + " 正在等待获取 lock 锁定的个数是:" + service2.lock.getQueueLength());
    }

}

结果是:

main 正在等待获取 lock 锁定的个数是:9

此时第一个线程 Thread-0 先执行 run 方法,然后一直在等待,此时后面的线程都在等待获取此 Lock 锁

3.getWaitQueueLength()和hasWaiters()

方法 getWaitQueueLength(Condition condition) 和方法 getQueueLength() 的作用类似,都是返回正在等待获取此锁定的线程估计数,只不过前者的方法需要传入参数 Condition,比如有 5 个线程,每个线程都执行了同一个 conditon 对象的 await() 方法,则调用该方法后返回的值是 5

hasWaiters(Condition condition) 方法用来查询是否有线程正在等待与 Lock 锁相关的 Condition 条件

class Service3 {

    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    private int count = 0;

    public void waitMethod() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " 正在执行 "
                    + count++);
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void notifyMethod() {
        try {
            lock.lock();
            System.out.println("是否有线程正在等待该Condition对象:" + lock.hasWaiters(condition));
            System.out.println("有 " + lock.getWaitQueueLength(condition)
                    + " 个线程正在等待 condition");
            System.out.println("当前线程调用 lock() 方法的次数:" + lock.getHoldCount());
            condition.signalAll();
            System.out.println("还有 " + lock.getWaitQueueLength(condition)
                    + " 个线程正在等待 condition");
            System.out.println("当前线程调用 lock() 方法的次数:" + lock.getHoldCount()
        } finally {
            lock.unlock();
        }
    }

}

public class Run3 {

    public static void main(String[] args) throws InterruptedException {
        Service3 service3 = new Service3();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                service3.waitMethod();
            }
        };

        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(runnable);
            threads[i].start();
        }

        Thread.sleep(2000);
        service3.notifyMethod();
    }

}

结果是:

Thread-0 正在执行 0
Thread-1 正在执行 1
Thread-2 正在执行 2
Thread-3 正在执行 3
Thread-4 正在执行 4
Thread-5 正在执行 5
Thread-6 正在执行 6
Thread-7 正在执行 7
Thread-8 正在执行 8
Thread-9 正在执行 9
是否有线程正在等待该Condition对象:true10 个线程正在等待 condition
当前线程调用 lock 方法的次数:1
还有 0 个线程正在等待 condition
当前线程调用 lock 方法的次数:1

该实例将多个线程同时绑定在一个 Condition 对象上,因此每个线程先获得 Lock 锁,然后调用同一个 Condition 对象的 await() 方法,表示每个线程都进入等待状态。线程 Thread-0 ~ 9 都占有着 Condition 对象,调用 hasWaiters() 方法输出 true,同时 getWaitQueueLength() 方法输出 10,表示共有 10 个线程在等待 Condition 对象。当我们使用 condition.signalAll() 语句将所有线程唤醒,此时就没有线程与 Condition 对象绑定了,也就没有线程等待该对象了

4.hasQueuedThread()、hasQueuedThreads()

方法 hasQueuedThread(Thread thread) 用来查询指定的线程是否正在等待获取 Lock 锁,需要传入想要查询的线程对象 Thread;方法 hasQueuedThreads() 用来查询是否有线程正在等待获取 Lock 锁定;方法 hasWaiters(Condition condition) 用来查询是否有线程正在等待 Lock 锁,需要传入 Condition 对象

class Service4 {

    public ReentrantLock lock = new ReentrantLock();
    public Condition condition = lock.newCondition();

    public void waitMethod() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " 正在执行");
            System.out.println("查询是否有线程正在等待与此锁定相关的Condition:" + lock.hasWaiters(condition));
            Thread.sleep(100000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

}

public class Run4 {

    public static void main(String[] args) throws InterruptedException {
        Service4 service4 = new Service4();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                service4.waitMethod();
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(2000);
        Thread thread1 = new Thread(runnable);
        thread1.start();
        Thread.sleep(2000);

        System.out.println(thread.getName() + "是否正在等待获取此锁定:"
                + service4.lock.hasQueuedThread(thread));
        System.out.println(thread1.getName() + "是否正在等待获取此锁定:"
                + service4.lock.hasQueuedThread(thread1));
        System.out.println("是否有线程正在等待获取此锁定:" + service4.lock.hasQueuedThreads());
    }

}

结果是:

Thread-0 正在执行
查询是否有线程正在等待与此锁定相关的Condition:false
Thread-0是否正在等待获取此锁定:false
Thread-1是否正在等待获取此锁定:true
是否有线程正在等待获取此锁定:true

线程 Thread-0 先执行 run 方法,拿到 Lock 锁,然后进入等待状态,由于线程 Thread-0 已经拥有 Lock 锁,因此 hasQueuedThread 输出 false,而线程 Thread-1 还没有执行 run 方法,也就没有拿到 Lock 锁,因此 hasQueuedThread 输出 true

5.isFair()、isHeldByCurrentThread()和isLocked()

方法 isFair() 用来判断当前的 Lock 锁是不是公平锁,方法 isHeldByCurrentThread() 用来查询当前线程是否保持该 Lock 锁,isLocked() 方法用来查询这个 Lock 锁是否由任意线程保持

class Service6 {

    public ReentrantLock lock;

    public Service6() {
        lock = new ReentrantLock();
    }

    public Service6(boolean isFair) {
        lock = new ReentrantLock(isFair);
    }

    public void serviceMethod() {
        try {
            System.out.println(Thread.currentThread().getName() + " 是否保持此锁定:"
                    + lock.isHeldByCurrentThread() + " " + lock.isLocked());
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " 是否保持此锁定:"
                    + lock.isHeldByCurrentThread() + " " + lock.isLocked());
            System.out.println(Thread.currentThread().getName()
                    + " 公平锁的情况:" + lock.isFair());
            System.out.println();
        } finally {
            lock.unlock();
        }
    }

}

public class Run6 {

    public static void main(String[] args) {
        Service6 service6 = new Service6(true);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                service6.serviceMethod();
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();

        Service6 service61 = new Service6(false);
        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                service61.serviceMethod();
            }
        };

        thread = new Thread(runnable1);
        thread.start();

        Service6 service62 = new Service6();
        Runnable runnable2 = new Runnable() {
            @Override
            public void run() {
                service62.serviceMethod();
            }
        };

        thread = new Thread(runnable2);
        thread.start();
    }

}

结果是:

Thread-0 是否保持此锁定:false false
Thread-0 是否保持此锁定:true true
Thread-0 公平锁的情况:true

Thread-1 是否保持此锁定:false false
Thread-1 是否保持此锁定:true true
Thread-1 公平锁的情况:false

Thread-2 是否保持此锁定:false false
Thread-2 是否保持此锁定:true true
Thread-2 公平锁的情况:false

ReentrantLock 类的构造函数,如果没有传入参数,则默认不是公平锁,如果传入 true,则是公平锁,如果传入 false,则不是公平锁

6.tryLock()和tryLock(long timeout,TimeUnit unit)

方法 tryLock() 表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回,在拿不到锁时不会一直在那等待

tryLock() 方法和 lock() 方法不同的是,如果线程使用 lock() 方法拿不到锁,那么该线程会一直等待下去,直到调用 unlock() 方法;而线程如果使用 tryLock() 方法拿不到锁,该线程不会一直等待,会立即返回,也不要使用 unlock() 方法来唤醒

该方法常见的用法是这样的:

boolean result = lock.tryLock();	//尝试获取到锁
if(result) {	//如果获取到了锁
    try {
        //具体操作
    } finally {
        lock.unlock();	//释放获取到的锁
    }
} else {	//如果没有获取到锁
    //具体操作
}

看个例子

class MyService10 {

    private ReentrantLock lock = new ReentrantLock();

    public void waitMethod() {
        boolean result = lock.tryLock();
        
        if (result) {
            System.out.println(Thread.currentThread().getName() + " 获得了锁 " + System.currentTimeMillis());
        } else {
            System.out.println(Thread.currentThread().getName() + " 没有获得锁 " + System.currentTimeMillis());
        }
    }

}

public class Run10 {

    public static void main(String[] args) throws InterruptedException {
        MyService10 service10 = new MyService10();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                service10.waitMethod();
            }
        };

        Thread thread = new Thread(runnable);
        thread.setName("AAA");
        thread.start();
		//让线程 AAA 执行完
        Thread.sleep(2000);

        Thread thread1 = new Thread(runnable);
        thread1.setName("BBB");
        thread1.start();
    }

}

结果是:

AAA 获得了锁 1541357008935
BBB 没有获得锁 1541357010935

可以看到,即使在线程 AAA 执行完2s后,线程 BBB还是没有获取到锁,说明此时线程 AAA 确实获取到了 Lock 锁,且没有释放该锁,线程 BBB 因为没有拿到锁,也没有一直等待下去,而是直接执行了返回 false 后面的语句

为了进一步证明线程 AAA 确实拿到锁了,我们可以将 if 后面的语句进行修改,其他条件保持不变

public void waitMethod() {
    if (lock.tryLock()) {
        try {
            System.out.println(Thread.currentThread().getName() + " 获得了锁 " + System.currentTimeMillis());
        } finally {
            System.out.println(Thread.currentThread().getName() + " 释放了锁 " + System.currentTimeMillis());
            lock.unlock();
        }
    } else {
        System.out.println(Thread.currentThread().getName() + " 没有获得锁 " + System.currentTimeMillis());
    }
}

再次执行,结果是:

AAA 获得了锁 1542099320375
AAA 释放了锁 1542099320375
BBB 获得了锁 1542099322375
BBB 释放了锁 1542099322376

可以看到,两个线程同时获得了锁,这时因为线程 AAA 在拿到锁之后,执行 lock.unlock() 语句释放了拥有的 Lock 锁,此时该锁没有任何线程拥有,2s后线程 BBB 尝试获取该锁,并且获取成功。这段代码更加证实了,tryLock() 方法确实能够获取锁

在平时使用 tryLock() 方法时,线程每次获取到了 Lock 锁之后要在 finally 语句块中及时关闭,就和使用 lock() 方法也要关闭一样

tryLock(long timeout,TimeUnit unit)

tryLock 方法表示如果在指定等待时间内获得了锁,就返回true,否则返回false。该方法也有一个InterruptedException 的异常说明,即如果在获取锁的时候被中断,也会直接抛出异常

class MyService11 extends ReentrantLock {

    public void waitMethod() {
        try {
            if (tryLock(5, TimeUnit.SECONDS)) {
                System.out.println(Thread.currentThread().getName() + " 获得了锁 "
                        + System.currentTimeMillis());
            } else {
                System.out.println(Thread.currentThread().getName() + " 没有获得锁"
                        + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

public class Run11 {

    public static void main(String[] args) throws InterruptedException {
        MyService11 service11 = new MyService11();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " 调用 waitmethod "
                        + System.currentTimeMillis());
                service11.waitMethod();
            }
        };

        Thread thread = new Thread(runnable);
        thread.setName("AAA");
        thread.start();

        Thread.sleep(2000);

        Thread thread1 = new Thread(runnable);
        thread1.setName("BBB");
        thread1.start();
    }

}

结果是:

AAA 调用 waitmethod 1541397775124
AAA 获得了锁 1541397775125
BBB 调用 waitmethod 1541397777126
BBB 没有获得锁1541397778126

其实该重载方法和之前的不加参数的方法类似,区别在于加了个时间范围,对于这个例子,线程 AAA 先获得了 Lock 锁,且没有释放,由于线程 BBB 在 5s 之内没能获得 Lock 锁,因此方法 tryLock(5, TimeUnit.SECONDS) 返回 false

7.lockInterruptibly()

lockInterruptibly() 方法的作用是,如果当前线程未被中断,则获取该锁定,如果已经被中断,则抛出异常。先来看一个没被中断的例子

class Service {

    public ReentrantLock lock = new ReentrantLock();

    public void waitMethod() {
        try {
            lock.lockInterruptibly();
            
            try {
                System.out.println(Thread.currentThread().getName()
                        + " 获得了锁 " + System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName()
                        + " 结束等待 " + System.currentTimeMillis());
            } finally {
                lock.unlock();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

public class Run {

    public static void main(String[] args) {
        Service service = new Service();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                service.waitMethod();
            }
        };

        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                service.waitMethod();
            }
        };

        Thread thread = new Thread(runnable);
        thread.setName("AAA");
        thread.start();

        Thread thread1 = new Thread(runnable1);
        thread1.setName("BBB");
        thread1.start();
      //thread1.interrupt();	//位置1
    }

}

结果是:

AAA 获得了锁 1542090080233
AAA 结束等待 1542090082234
BBB 获得了锁 1542090082234
BBB 结束等待 1542090084235

可以看到,因为此时两个线程都没有发生中断,因此,线程 AAA 先执行了 lock.lockInterruptibly() 拿到 lock 锁,此时线程 BBB 因为没能拿到该锁,因此只能等待,等待线程 AAA 执行完 run 方法并释放对象锁之后,线程 BBB 才可以拿到对象锁,并执行。

即在没有线程被中断的情况下,方法 lockInterruptibly 就相当于 lock 方法,都能获取 lock 锁,也都能释放 lock 锁。我们一般使用这样的格式:

try {
    lock.lockInterruptibly();
    
    try {
        // 具体代码
    } finally {
        lock.unlock();	//释放锁
    }
} catch (InterruptedException e) {
    e.printStackTrace();
}

第一个 try 方法块用来捕获 lockInterruptibly 可能抛出的 InterruptedException 异常,第二个 try 方法块用来放执行的具体代码和释放 lock 锁

在来看一个中断的例子,对上面的例子上稍加改动,将位置1上注释拿掉,同时在 catch 块中中添加下面的代码:

try {
    ...
} catch (InterruptedException e) {
    System.out.println(Thread.currentThread().getName()
                       + " 发生中断,抛出异常 " + System.currentTimeMillis());
}

结果是:

AAA 获得了锁 1542090865278
BBB 发生中断,抛出异常 1542090865279
AAA 结束等待 1542090867279

这次的结果和之前的结果大不相同,同样是线程 AAA 先调用 lock.lockInterruptibly() 拿到 lock 锁,当线程 BBB 调用 lock.lockInterruptibly() 的时候,肯定是被阻塞的,但是同时,在线程 main 中执行了 thread1.interrupt() 方法,即对线程 BBB 发生中断信号,此时还处于阻塞状态的线程 BBB 立刻抛出 InterruptedException 异常,并且打印

至于为什么会这样,可以参照我的另一篇博文 https://blog.csdn.net/babycan5/article/details/84031201 ,这里不加赘述

参考

《Java多线程编程核心技术》
https://blog.csdn.net/yinbucheng/article/details/51626131
https://mp.weixin.qq.com/s?__biz=MzIxNTQ3NDMzMw==&mid=2247483879&idx=1&sn=5167ed2e7b1804f741fac39318e657aa&scene=19#wechat_redirect

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值