基本概念
synchronized是Java的内置锁,synchronized加锁和释放锁由jvm控制,不需要人为控制,所以使用的时候非常简单,但synchronized锁也存在许多的缺点。
1.不够灵活,不能跨方法加锁
2.当线程尝试获取synchronized锁的时候,如果获取不到锁会一直阻塞,这个阻塞的过程,用户无法控制
Reentrantlock是java.util.concurrent包下的可重入锁,可中断锁,默认是非公平锁。它的获取锁与释放锁由人手动控制,并且提供了更加细粒度的加锁方法,弥补了一些synchronized加锁方式的缺点。
基本使用
ReentrantLock lock = new ReentrantLock();//创建锁
lock.lock();//加锁
try{
//逻辑代码
}finally{
lock.unlock();//释放锁
//释放锁的操作一定要放在finally中,否则,程序出现了异常,锁没有释放,那么其他线程就再也没有机会获取这个锁了。
}
ReentrantLock是可重入锁
可重入锁:重入锁是指同一个线程可以多次获得同一把锁;ReentrantLock和关键字Synchronized都是可重入锁
ReentrantLock lock = new ReentrantLock();
lock.lock();
lock.lock();
try{
//逻辑代码
}finally{
lock.unlock();//释放锁
lock.unlock();
}
注意:
1. lock()方法和unlock()方法需要成对出现,锁了几次,也要释放几次,否则后面的线程无法获取锁了;可以将add中的unlock删除一个事实,上面代码运行将无法结束
2. unlock()方法放在finally中执行,保证不管程序是否有异常,锁必定会释放
ReentrantLock实现公平锁
看一下jdk中ReentrantLock的源码,2个构造方法:
public ReentrantLock(){
sync = new NonfairSync();
}
public ReentrantLock(boolean fair){
sync = fair ?new FairSync():new NonfairSync();
}
默认构造方法创建的是非公平锁。第2个构造方法,有个fair参数,当fair为true的时候创建的是公平锁.
//公平锁
ReetrantLock lock = new ReetrantLock(true);
ReentrantLock获取锁的过程是可中断的
如果需要在获取锁的过程中能够中断,需要使用ReentrantLock的lockInterruptibly()获取锁,当前线程获取锁之后,在调用线程的interrupt()方法,将中断标准变为ture后,就会引发InterruptedException异常。
ReentrantLoca lock = new ReentrantLock();
Thread t = new Thread(()->{
try{
lock.lockInterruptibly();//获取锁
//逻辑代码
}catch(InterruptedException e){
System.out.println("中断标志"+this.isInterrupted());
e.printStackTrace();
}finally{
if(lock.isHeldByCurrentThread){//当前线程是否获取了锁
lock.unlock();
}
}
});
t.start();
t.interrupt();
线程调用interrupt()之后,线程的中断标志会被置为true,触发InterruptedException异常之后,线程的中断标志有会被清空,即置为false,所以当线程调用interrupt()引发InterruptedException异常,中断标志的变化是:false->true->false
ReentrantLock锁申请等待限时
ReentrantLock提供了个tryLock(),可以选择传入时间参数,表示等待指定的时间,无参则表示立即返回锁申请的结果:true表示获取锁成功,false表示获取锁失败。
//tryLock无参
public boolean tryLock() //尝试去获取锁,没有获取到立即返回
//tryLock有参
public boolean tryLock(long timeout,TimeUnit unit) throws InterruptedException
//获取锁超时时间设置为3秒,3秒内是否能否获取锁都会返回
lock.tryLock(3,TimeUnit.SECONDS)
有参的tryLock()方法,第一个参数是时间大小,第二个参数是是时间类型,是一个枚举,可以表示时、分、秒、毫秒等。
获取锁的4种方法对比
获取锁的方法 | 是否立即响应(不会阻塞) | 是否响应中断 |
---|---|---|
lock() | × | × |
lockInterruptibly() | × | √ |
tryLock() | √ | × |
tryLock(long timeout, TimeUnit unit) | × | √ |