ReentrantLock相对于synchronized来说自身存在一个缺点
synchronized的加锁和解锁是一体的,只要代码执行进入到加锁代码块就会自动进行加锁,相对的一但跳出代码块也就会自动进行解锁.
Object object = new Object();
//进入代码块加锁
synchronized (object){
}
//出代码块解锁
而ReentrantLock的加锁和解锁则是不同的两个方法,分别是:
ReentrantLock reentrantLock = new ReentrantLock();
//加锁
reentrantLock.lock();
//解锁
reentrantLock.unlock();
而ReentrantLock这中开锁和解锁分开的形式,就会导致出现当代码加锁以后解锁之前代码遇到某些问题,导致代码没有执行到 unlock方法之前就结束了,这样就会导致锁一直得不到释放.
而为了避免这种情况的发生我们一般会采取try/finally与lock/unlock一起使用来解决这个问题
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
try {
//加锁
reentrantLock.lock();
} finally {
//解锁
reentrantLock.unlock();
}
这样就可以解决这个问题,无论什么情况下锁都可以得到释放.
ReentrantLock相对于synchronized来说三个优点
优势一
ReentrantLock中包含一个tryLock方法,避免了"死等策略给我们带来的问题",给我们提供了更多的可能.
ReentrantLock reentrantLock = new ReentrantLock();
//尝试加锁(不带参数)
reentrantLock.tryLock();
//尝试加锁(带参数) ---- 可以指定等待时间(timeout) 及时间单位(TimeUnit)
reentrantLock.tryLock(1000, TimeUnit.SECONDS);
}
顾名思义tryLock就是尝试加锁的意思.
不带参数的tryLock方法会尝试加锁如果加锁失败就会立刻放弃加锁不做任何等待.
带参数的tryLock方法会一直进行尝试进行加锁,如果超过了给定的最大等待时间就会放弃加锁.
优势二
ReentrantLock本身是一个非公平锁,但是可以通过再构造实例对象是传入参数来是其成为公平锁
而synckronized本身就是一个非公平锁,并且也无法改变.
所谓公平即遵循"先来后到"
ReentrantLock reentrantLock = new ReentrantLock(true);//传入参数"true"实现公平锁
优势三
synchronized需要搭配wait/notify方法来进行等待唤醒操作,但是有多个线程进行等待时调用notify方法只能随机唤醒其中一个线程.而并不能具体指定一个线程来进行唤醒.
而ReentrantLcok搭配Condition类来进行唤醒等待操作时,就可以具体指定一个线程来进行唤醒.
synchronized相对于ReentrantLock来说两者还有一个无关紧要的区别就是
synchronized是一个Java关键字,底层是JVM来实现的(通过C++来实现的)
ReentrantLock是一个Java标准库中的一个类,是基于Java代码来进行实现的.