1.ReentrantLock是什么
在说ReentrantLock之前,必须先说一说锁。锁是为了线程安全而诞生的,我们常用的锁就是synchronized,通过下面程序看一下什么叫锁,锁有什么用。下面我们模拟一下数据库主键自增,假如我们现在有10个线程,每个线程对count进行自增,确保主键的唯一性,而事实却是当只有一个线程时却是没有问题,当多线程时却有着无法预估的错误。
可以看出来此时已经导致生成的主键重复了,原因是当线程在做自增时,同时存在其他线程也在做自增,虽然count 只有一条语句,而实际上count 的操作分成了3步,先读取数据,然后加1,最后写入;当线程A读取为1时修改为2,但是可能还没有写回去,那么就存在线程2也读到的也是1然后自增,所以会出现多个线程之间存在相同的值。
解决办法很多最常用的方式就是使用Java的关键字synchronized进行加锁操作。使用synchronized加锁后每个线程都会先去获取锁,然后执行完代码最后释放锁,另外的线程才能拿到锁。这样每次执行操作时实际上只有一个线程在操作。
实际上synchronized不仅可以加方法上,实际上也可以是this或者对象或者xx.class。但是这几者之间是有区别的,当加在非静态的方法上时和this是一样的都是锁的当前对象,而加在静态方法上时则和xx.class一样锁的是当前类的class。
2.ReentrantLock的使用
了解了锁的基本用法后我们来看看ReentrantLock,两者的异同如下。
1.synchronized和ReentrantLock都是独占锁。且都是可重入锁。
2.synchronized的是非公平锁,而ReentrantLock可实现公平锁或非公平锁。
3.synchronized加锁和释放锁都是自动的,而ReentrantLock则是需要手动获取锁,以及释放锁。 4.synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。
5.相比较于synchronized来说ReentrantLock实现基于CAS操作,而synchronized则是基于自旋锁,偏向锁以及向操作系统os申请重量级锁的实现方式。
2.1 ReentrantLock实现锁机制
可以看出来和synchronized的区别就是需要手动获取锁,也需要手动释放锁,所以释放锁的代码必须放finally里面,确保一定会释放,否则其他线程无法获取锁。
2.2 公平锁实现
所谓的公平锁就是指等待时间长的先获得锁,ReentrantLock默认为非公平锁,可通过构造函数指定为true设置为公平锁。而非公平锁则是由CPU随机调度,cpu时间片轮到哪个线程,哪个线程就能获取锁。
2.3 尝试获取锁 tryLock
当线程1启动后进行休眠,由于没有让出锁,此时线程2通过tryLock尝试在3秒内看是否能获取锁,若获得锁则返回true,未获取到则返回false。基于tryLock实际上我们可以用于检测是否死锁。当尝试3秒内是否能获取到锁时输出如下。
当设置尝试8秒内能不能获取到锁时输出结果如下
2.4 lockInterruptibly,lockInterruptibly的作用是如果一直尝试获取不到锁,可以使用另外一个线程来打断等待。