写点什么

2021-06-05# Java 基础 (dayFourteen):锁的两种方式

  • 2022 年 5 月 11 日
  • 本文字数:1703 字

    阅读完需:约 6 分钟

可以看到在 lock 前面的代码是可以让其他线程执行的,但是 lock 后面的代码必须等待前面的线程释放锁才可以去执行,期间就会发生阻塞


这里要注意的是,每个对象会有自己的 ReentrantLock,所以要争抢同一个 ReentrantLock 对象才会发生阻塞,该锁就可以保证串行化访问,如果是两个不同的对象,以上个栗子为例,就是两个不同的 TestSyncFunny 对象,那么两个线程得到的是不同的锁,是不会发生阻塞现象的


这个锁称为重入锁(ReentrantLock:重入),因为这个锁可以被线程反复获得,该锁会有一个持有计数来跟踪对 lock 方法的嵌套使用,即在加锁的 try 语句块里面调用方法 A,该方法 A 加的是同一把锁(同一个 ReentranLock),所以获得 B 的锁也会去影响 A 的运行


举个栗子


private Ree 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 ntrantLock lock = new ReentrantLock();


public void doSyncTo(String name){


System.out.println("下面"+name+"也要进行加锁");


lock.lock();


try{


System.out.println(name+"已经加锁了~~~~");


Thread.sleep(2000);


} catch (InterruptedException e) {


e.printStackTrace();


} finally {


lock.unlock();


}


}


public void doSync(String name){


System.out.println(name+"还没进行上锁----");


lock.lock();


try{


System.out.println(name + "上锁了,dododoodid");


doSyncTo(name);


System.out.println(name+"调用完了另一个加锁方法,并且已经释放那个锁了");


Thread.sleep(2000);


System.out.println(name+"睡醒了");


//code....


} catch (InterruptedException e) {


e.printStackTrace();


} finally {


lock.unlock();


}


}


上面是两个加锁的方法,用都是同一个 ReentrantLock,而且在 doSync 方法中,还调用了 doSyncTo 的方法



可以看到,调用 doSync 拿到的 ReentrantLock,会让其他线程也无法调用 doSyncTo 加锁后的那些方法,必须要等线程释放了最外的一层锁,即 doSync 的 lock 才可以调用,单独释放了 doSyncTo 里面的 lock 也是不够的

[](()公平锁

我们也可以使用 ReentrantLock 来构建一个公平锁


调用下面的构造方法,传入 true 参数



公平锁是指:锁释放的时候,倾向于让等待时间最长的线程获取该锁


不过公平锁会影响性能,所以一般情况下都是非公平的,而且公平锁不一定是绝对公平的,还要取决于线程调度器,如果线程调度器忽略了一个已经为锁等待很长时间的线程,那就不能实现公平

[](()条件对象

假设出现了某个线程进入了临界区后,需要等待其他线程完成某些操作才可以继续执行,那么如果这个线程拿到了 ReentrantLock,那么其他线程拿不到 ReentrantLock,不能进行执行命令,不可能完成指定操作,让拿到了锁的线程继续执行下去,那么这个情况该如何解决


对此的解决方法就是,让该线程现在暂停,并且放弃对锁的持有,让其他线程完成操作,当达到条件后,该线程才可以重新激活,从之前暂停的地方进行下面的操作


所以我们可以给其加上一个条件对象,当不满足条件的时候,让线程在这里进行等待并且释放锁,当满足条件时,而且锁可用的时候,该线程不会立马去争夺该锁从而变成可运行的状态,然后执行剩下的操作,而是当有另一个线程同样判断是否满足条件的时候,才会重新激活等待这个条件的所有线程(这些线程是没有办法自己激活自己的),也正因为这个,经常会导致死锁的现场,假设修改条件的线程发生了阻塞,而等待条件的线程进行了 await,那么这两个线程都完蛋了


获取条件对象



条件对象是一个 Condition,调用 await 方法就可以进入线程的等待集(等待条件满足被激活)中,当条件满足后,另一个进行同样判断的线程(也是进入了 while(number < 9)的线程)就会去激活等待集里的所有线程,然后这些线程才能去抢锁,抢到锁才可以继续从暂停的地方继续执行下去


对于 ReentrantLock 的条件对象可以使用多个,从而形成不同的线程等待集

[](()synchronized 关键字

从 java1.0 版本开始,Java 中的每个对象都有一个内部锁,也成为内部对象锁,使用 synchronized 关键字


使用该关键字就不用显示地去使用一个重入锁(ReentrantLock),而内部对象锁只能由一个关联条件,也就是只有一个条件对象(区别于 ReentrantLock 可以有多个条件对象)


对应条件对象相关的方法如下

用户头像

还未添加个人签名 2022.04.13 加入

还未添加个人简介

评论

发布
暂无评论
2021-06-05# Java基础(dayFourteen):锁的两种方式_Java_爱好编程进阶_InfoQ写作社区