ReentrantLock基本用法

1.相对于synchronized具备的特点:

1) 可打断:避免死等
2)可以设置超时时间:避免死等
3)可以设置为公平锁:防止饥饿
4)支持多个条件变量:可以指定唤醒某个特定线程
5)需要手动释放锁:如果执行过程中出现异常,并不会自动释放锁, 需要写finally块手动释放锁

2. 可重入:

设置ReentrantLock为类的静态变量,在main线程中调用m1()方法,m1()方法加锁reentrantlock.lock(),m1中又调用m2(),m2也加锁,m2()调用m3(),m3方法中也加锁。

static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) {
 method1();
}
public static void method1() {
 reentrantLock.lock();
 try {
 log.debug("execute method1");
 method2();
 } finally {
 reentrantLock.unlock();
 }
}
public static void method2() {
 reentrantLock.lock();
 try {
 log.debug("execute method2");
 method3();
 } finally {
 reentrantLock.unlock();
 }
}
public static void method3() {
 reentrantLock.lock();
 try {
 log.debug("execute method3");
 } finally {
 reentrantLock.unlock();
 }
}

运行结果:说明ReentrantLock是可重入锁

3.可打断(打断等待):

当t1线程使用synchronized加锁时,如果t2线程获取不到锁,只能自旋等待或者进入EntryList中等待,无法打断等待

ReentrantLock锁可以打断t2线程的等待

下面代码的解释:

定义类变量ReentrantLock,t1线程使用lockInterruptibly()方法加锁,然后睡眠,睡眠不会释放锁。

t2线程在t1线程睡眠时,获得时间片,尝试获得reentrantlock锁,没有获得成功,进入阻塞队列等待,调用t2.interrupt()方法打断t2线程的等待,进入catch块,打印"t2没有获得到锁,被打断,结束t2线程等待"和异常信息

使用 lockInterruptibly()方法加锁,需要把加锁代码放在try块中,当被打断等待时,执行catch块中的代码,打印异常信息,并且return结束当前线程

private static ReentrantLock reentrantLock=new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {

        Thread t1=new Thread(()->{
            try {
                reentrantLock.lockInterruptibly();
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("t1没有获得到锁,被打断,结束t1线程等待");
                return;
            }
            try {
                System.out.println("t1获得到锁");
                System.out.println("hello,t1");
                Thread.sleep(1000000);
            }catch (Exception e){
                System.out.println(e);
            }finally {
                reentrantLock.unlock();
            }
        });


        Thread t2=new Thread(()->{
            try {
                reentrantLock.lockInterruptibly();
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("t2没有获得到锁,被打断,结束t2线程等待");
                return;
            }
            try {
                System.out.println("t2获得到锁");
                System.out.println("hello,t2");
            }catch (Exception e){
                System.out.println(e);
            }finally {
                reentrantLock.unlock();
            }
        });

        t1.start();
        Thread.sleep(1);
        t2.start();
        t2.interrupt();


    }

4.设置超时时间:

tryLock(1,TimeUnit.SECONDS):是尝试获取锁1s,返回一个boolean结果,通过结果判断是否继续向下执行。

需要放在try块中,此方法也是可以被打断的,被打断后执行catch块中的内容。

注意:try和catch中都需要return,如果此方法被打断后,进入catch,如果catch中没有return,则会继续执行try..catch块外面的内容,如图中的打印hello

private static ReentrantLock reentrantLock=new ReentrantLock();
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            try {
                if(!reentrantLock.tryLock(10, TimeUnit.SECONDS)){
                    System.out.println("没有获取到锁");
                    return;
                }else{
                    System.out.println("获取到锁");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("获取不到锁");
                return;
            }
            System.out.println("hello");
        });

        reentrantLock.lock();
        t1.start();
    }

5.公平锁

使用synchronized加锁时,如果t1线程加上锁,t2,t3,t4线程则需要进入EntryList中等待,t1线程释放锁,唤醒t2,t3,t4 ,但t2,t3,t4不会按照先进先出的顺序获得锁,会非公平的抢占锁。

ReentrantLock reentrantLock = new ReentrantLock(true); 设置为true,则reentrantLock变为公平锁,即获得锁的顺序按照先进先出的顺序。

6.条件变量/await()/signal()

如果用synchronized对object加锁,如果t1线程获得了锁,但是不满足继续执行的条件,只能调用object.wait()方法,放弃锁,如果有多个线程不满足条件,都会放弃锁进入WaitSet中等待

一旦某个线程的条件满足,需要唤醒时,没办法特定唤醒某个线程,只能随机唤醒object.notify(),或者全部唤醒object.notifyAll()

如果用ReentrantLock加锁,可以使用多个条件变量,特定唤醒某个线程。

如下代码的含义:

t1线程先获取reentrantlock锁,但是c1是false,不满足条件继续执行,Condition C1condition=reentrantLock.newCondition()创建一个t1线程的特定条件,调用C1condition.await()方法放弃锁。

主线程获得reentrantlock锁,将c1设置为true,唤醒t1线程,调用C1condition.signal()方法,这样可以指定唤醒t1线程,t1线程继续向下执行。

 注意:使用await()和signal()时,与synchronized的wait()和notify()一样,也需要提前获取锁对象。

static ReentrantLock reentrantLock=new ReentrantLock();
    static volatile boolean c1=false;
    static Condition C1condition=reentrantLock.newCondition();

    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            reentrantLock.lock();
                try {
                    System.out.println("t1线程获得锁");
                    if(!c1){
                        System.out.println("t1线程不满足条件,放弃锁");
                        C1condition.await(100,TimeUnit.SECONDS);
                    }
                    System.out.println("t1线程继续执行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    reentrantLock.unlock();
                }

        });
        t1.start();

        Thread.sleep(1000);
        reentrantLock.lock();
        c1=true;
        C1condition.signal();
        reentrantLock.unlock();

    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值