2021-06-05# Java 基础 (dayFourteen):锁的两种方式
可以看到在 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 可以有多个条件对象)
对应条件对象相关的方法如下
评论