【JAVA核心知识】21-F.1:ReentrantLock的简单使用

ReentrantLock的简单使用

加锁与解锁

lock(),lockInterruptibly(),tryLock(long timeout, TimeUnit unit),tryLock()的区别

lock(),tryLock()与unlock()的简单使用:

lock(),lockInterruptibly(),tryLock(long timeout, TimeUnit unit)对中断的反应:

ReentrantLock的其它常用方法

示例代码

关联导航 


ReentrantLock是JDK1.5引入的一个API级别的可重入锁。不同于synchronized仅支持非公平模式,ReentrantLock可以在构造时指定这个锁是公平模式还是非公平模式。除了能实现synchronized所能完成的工作外,还提供了如可响应中断锁,可轮询锁请求,定时锁等避免多线程死锁的方法。

加锁与解锁

lock(),lockInterruptibly(),tryLock(long timeout, TimeUnit unit),tryLock()的区别

ReentrantLock执行加锁有4个方法,分别为lock(),lockInterruptibly(),tryLock(long timeout, TimeUnit unit),tryLock(),4个方法的区别为:

  • lock(): 一直等待锁,无视中断,在获取锁过程中即使线程遭遇中断也不会管,而是继续等下去,获得锁之后线程继续执行而不是中断
  • lockInterruptibly():一直等待锁,考虑中断,获取锁过程中如果线程遭遇中断,则中断等待,则抛出InterruptedException,因此要求必须需要捕获InterruptedException
  • tryLock(long timeout, TimeUnit unit): 指定时间等待锁,指定时间获取锁成功返回true,获取失败返回false。等待时间内考虑中断,等待过程中如果遭遇中断会抛出InterruptedException,因此要求必须需要捕获InterruptedException
  • tryLock(): 直接获取锁,成功返回true,获取失败返回false。等价于tryLock(0, TimeUnit unit);

ReentrantLock执行解锁均使用unlock()方法,unlock()必须由持有锁的线程执行,否则抛出IllegalMonitorStateException。为了避免死锁,unlock()最好放在finally块内。因为ReentrantLock是一个可重入锁,因此一个线程可能进行了多次加锁,但是unlock()执行一次仅解锁一次,也就是说持有锁的线程加了多少次锁,也要用unlock()解锁多少次,否则锁依然不会被释放!

lock(),tryLock()与unlock()的简单使用:

public class SimReentrantLock {

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("t1获取锁");
                    lock.lock(); // 加锁
                    System.out.println("t1拿到锁");
                    System.out.println("1号逻辑");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("1error");
                    e.printStackTrace();
                } finally {
                    lock.unlock(); // 解锁
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                boolean tryFlag = lock.tryLock();
                System.out.println("t2用tryLock获取锁的标识:" + tryFlag);
                if (!tryFlag) {
                    try {
                        System.out.println("t2用lock获取锁");
                        lock.lock();
                        System.out.println("t2拿到锁");
                    } catch (Exception e) {
                        System.out.println("2error");
                        e.printStackTrace();
                    } finally {
                        lock.unlock(); // 解锁
                    }
                } else {
                    System.out.println("t2用tryLock拿到了锁");
                    lock.unlock(); // 解锁
                }
            }
        });
        t1.start();
        try {
            Thread.sleep(10); // 这里睡眠一下保证1号线程先拿到锁
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        t2.start();
    }

}

运行结果:

t1获取锁
t1拿到锁
1号逻辑
t2用tryLock获取锁的标识:false
t2用lock获取锁
t2拿到锁

lock(),lockInterruptibly(),tryLock(long timeout, TimeUnit unit)对中断的反应:

public class LockAndLockInterruptibly {

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("t1获取锁");
                    lock.lock(); // 加锁
                    System.out.println("t1拿到锁");
                    System.out.println("1号逻辑");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("1error");
                    e.printStackTrace();
                } finally {
                    lock.unlock(); // 解锁
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    System.out.println("t2获取锁");
                    lock.lock();// 一直等待锁,无视中断,在获取锁过程中即使线程遭遇中断也不会管,而是继续等下去,获得锁之后线程继续执行而不是中断
//                    lock.lockInterruptibly(); // 一直等待锁,考虑中断,获取锁过程中如果线程遭遇中断,则中断等待,则抛出InterruptedException,因此要求必须需要捕获InterruptedException
//                    lock.tryLock(200, TimeUnit.MILLISECONDS);// 指定时间等待锁,指定时间获取锁成功返回true,获取失败返回false。等待时间内考虑中断,等待过程中如果遭遇中断会抛出InterruptedException,因此要求必须需要捕获InterruptedException
                    System.out.println("t2拿到锁");
                    System.out.println("2号逻辑");
                } catch (Exception e) {
                    System.out.println("2error");
                    e.printStackTrace();// 这个地方打出来的异常有些许不同
                } finally {
                    lock.unlock(); // 解锁
                }
            }
        });
        t1.start();
        try {
            Thread.sleep(10); // 因为用2号线程测试lock(),lockInterruptibly(),tryLock()的区别,所以这里睡眠一下保证1号线程先拿到锁
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        t2.start();
        try {
            Thread.sleep(10); // 2号线程睡一下保证运行到lock(),lockInterruptibly(),tryLock()
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        t2.interrupt(); // 2号线程执行中断
    }
}

2号线程使用lock()的运行结果:

t1获取锁
t1拿到锁
1号逻辑
t2获取锁
t2拿到锁
2号逻辑  // 可以看到无视了中断,拿到锁执行了2号逻辑

2号线程使用lockInterruptibly()的运行结果:

t1获取锁
t1拿到锁
1号逻辑
t2获取锁
2error  // 可以看到2号线程响应了中断并抛出了InterruptedException
java.lang.InterruptedException  

2号线程使用tryLock(long timeout, TimeUnit unit)的运行结果:

t1获取锁
t1拿到锁
1号逻辑
t2获取锁
2error // 可以看到在等待时间内响应了中断并抛出了InterruptedException
java.lang.InterruptedException

ReentrantLock的其它常用方法

  • int getHoldCount():当前锁的加锁次数,如果无线程持有锁就返回0
  • int getQueueLength(): 当前竞争此锁队列长度
  • int getWaitQueueLength(Condition condition): 当前锁等待队列长度。需要与Condition联合使用
  • boolean isFair(): 是否是公平锁
  • boolean isLocked(): 当前锁是否处于锁定状态
  • boolean isHeldByCurrentThread(): 当前锁是否被当前线程持有
  • boolean hasWaiters(Condition condition): 当前condition是否有线程在等待,需要与Condition联合使用
  • boolean hasQueuedThread(Thread thread): 目标线程是否在这个锁的竞争队列中
  • boolean hasQueuedThreads(): 是否有线程在当前锁的竞争队列中

示例代码

public class OtherMethodOfRTL {
    public static void main(String[] args) {
        final ReentrantLock lock = new ReentrantLock();
        final Condition c = lock.newCondition();
        
        final Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    System.out.println("T1得到锁");
                    System.out.println("T1开始等待");
                    c.await();
                    System.out.println("T1收到信号");
                    System.out.println("T1发出信号");
                    c.signal();
                } catch (InterruptedException e) {
                    System.out.println("1error");
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });
        for (int i = 0; i < 3; i++) {
            final int j = i;
            final Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        System.out.println(j + "拿到锁");
                        System.out.println("持有加锁次数:" + lock.getHoldCount());
                        System.out.println("当前竞争此锁队列长度:" + lock.getQueueLength());
                        System.out.println("当前锁等待队列长度:" + lock.getWaitQueueLength(c));
                        System.out.println("是否是公平锁:" + lock.isFair());
                        System.out.println("当前锁是否有线程持有:" + lock.isLocked());
                        System.out.println("当前锁是否被当前线程持有:" + lock.isHeldByCurrentThread());
                        System.out.println("当前condition是否有线程在等待:" + lock.hasWaiters(c));
                        System.out.println("目标线程是否在这个锁的竞争队列中:" + lock.hasQueuedThread(t1));
                        System.out.println("是否有线程在当前锁的竞争队列中:" + lock.hasQueuedThreads());
                        System.out.println(j + "等待信号");
                        c.await(); 
                        System.out.println(j + "收到信号");
                        System.out.println("当前condition是否有线程在等待:" + lock.hasWaiters(c));
                        System.out.println("目标线程是否在这个锁的竞争队列中:" + lock.hasQueuedThread(t1));
                        System.out.println("是否有线程在当前锁的竞争队列中:" + lock.hasQueuedThreads());
                        System.out.println(j + "发出信号");
                        c.signal();
                    } catch (Exception e) {
                        System.out.println("2error");   
                        e.printStackTrace();   
                    } finally {
                        lock.unlock(); // 一定记得要解锁!!!
                    }
                }
            });
            t.start();
        }
        
        t1.start();
        lock.lock();
        System.out.println("主线程拿到锁");
        try {
            Thread.sleep(100); // 睡一下保证确实有线程在等待了,免得线程都没到wait,唤醒了个寂寞
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程发出信号");
        c.signal();   // 唤醒
        System.out.println("主线程释放锁");
        lock.unlock();
    }
}

运行结果:

0拿到锁
持有加锁次数:1
当前竞争此锁队列长度:4
当前锁等待队列长度:0
是否是公平锁:false
当前锁是否有线程持有:true
当前锁是否被当前线程持有:true
当前condition是否有线程在等待:false
目标线程是否在这个锁的竞争队列中:true
是否有线程在当前锁的竞争队列中:true
0等待信号
2拿到锁
持有加锁次数:1
当前竞争此锁队列长度:3
当前锁等待队列长度:1
是否是公平锁:false
当前锁是否有线程持有:true
当前锁是否被当前线程持有:true
当前condition是否有线程在等待:true
目标线程是否在这个锁的竞争队列中:true
是否有线程在当前锁的竞争队列中:true
2等待信号
1拿到锁
持有加锁次数:1
当前竞争此锁队列长度:2
当前锁等待队列长度:2
是否是公平锁:false
当前锁是否有线程持有:true
当前锁是否被当前线程持有:true
当前condition是否有线程在等待:true
目标线程是否在这个锁的竞争队列中:true
是否有线程在当前锁的竞争队列中:true
1等待信号
主线程拿到锁
主线程发出信号
主线程释放锁
T1得到锁
T1开始等待
0收到信号
当前condition是否有线程在等待:true
目标线程是否在这个锁的竞争队列中:false
是否有线程在当前锁的竞争队列中:false
0发出信号
2收到信号
当前condition是否有线程在等待:true
目标线程是否在这个锁的竞争队列中:false
是否有线程在当前锁的竞争队列中:false
2发出信号
1收到信号
当前condition是否有线程在等待:true
目标线程是否在这个锁的竞争队列中:false
是否有线程在当前锁的竞争队列中:false
1发出信号
T1收到信号
T1发出信号

关联导航 

【JAVA核心知识】系列导航 [持续更新中…]
关联导航:22-F.1:Condition的简单使用
关联导航:21:从源码看ReentrantLock
欢迎关注…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yue_hu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值