相比较synchronized而言 ReentrantLock有以下特点:
- 可中断
- 可以设置超时时间
- 可以设置为公平锁(防止线程饥饿)
- 支持多个条件变量
- 与synchronized 一样支持可重入
基本语法
首先需要创建一个ReentrantLock对象,以及获取锁,在try里面执行临界区代码,在finally里面释放锁,值得注意的是lock和unlock一定要成对出现
ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.lock();//获取锁
try{
//临界区
}finally {
reentrantLock.unlock();
}
可重入
同synchronized一样,ReentrantLock是支持重入的如下所示
static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) {
reentrantLock.lock();//获取锁
try{
log.debug("访问到Main的ReentrantLock的临界区");
method();
}finally {
reentrantLock.unlock();
}
}
public static void method(){
reentrantLock.lock();//获取锁
try{
log.debug("访问到method的ReentrantLock的临界区");
}finally {
reentrantLock.unlock();
}
}
可打断
- ReentrantLock提供了可打断的功能,是提供了lockInterruptibly()方法
- lockInterruptibly()表示如果没有竞争就获取锁,如果有竞争就会进入阻塞队列中去,它与lock的区别是,lock进入阻塞队列是一直等待释放锁,lockInterruptibly()是可以被打断的
- 如果通过interrupt()打断了,这时候线程是可以继续往下接着执行,但是是没有获取到锁的,如果还继续执行释放锁(unlock),就会报错,一般使用lockInterruptibly()的正确姿势是,如果被打断了在捕捉异常里直接return,如果没有被打断,在try{}finally{}里释放锁如下代码所示:
static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
log.debug("尝试获取锁");
reentrantLock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("被打断了");
return;
}
log.debug("打断了之后还能接着执行么");
try{
log.debug("获取到锁");
}finally {
reentrantLock.unlock();
}
}
},"t1");
reentrantLock.lock(); //主线程先获取锁
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.interrupt();
}
锁超时
- ReentrantLock的tryLock方法 可以解决的死锁问题
- 它首先尝试获取锁,如果没有没有获取到锁会返回false
- 同时它可以使用tryLock(long n,TimeUnin)来表示如果获取锁的时候出现竞争,最多等待多久
- 如果使用带参数的tryLock,一定要捕捉打断异常
公平锁
- 为了解决锁饥饿的问题,可以使用ReentrantLock的公平锁机制来实现
- 在构造方法中传入true,就可以实现公平锁机制
- 尽量少使用公平锁,这会影响线程的并发度
条件变量
- 在synchronized的多把锁中,可以对一个BigRoom,切成多个小Room,这样针对每一个小Room进行加锁,访问临界区的线程针对小Room的Monitor进入等待或者被唤醒,这个WaitSet就是条件变量
- 到ReentrantLock中可能就没有那么复杂了,不会让使用者自己使用面向对象的方式创建一个个的Room,它有Condition来代替了自创建Room
- 通过ReentrantLock.newCondition() 就能创建一个条件变量
- Condition使用await&signal来通知该条件变量上的线程,与wait&nofity一样,使用此方法必须先获得锁
网友评论