参考:https://www.bilibili.com/video/BV1ta4y1H73X?spm_id_from=333.999.0.0
点击查看【bilibili】
Lock 接口
// 获取锁, 未获取成功会一直等待
void lock();
// 获取锁,如果等待锁过程中被中断,会放弃等待锁并抛出中断异常
void lockInterruptibly() throws InterruptedException;
// 获取锁,立即返回结果
boolean tryLock();
// 在一段时间内获取锁,在此时间内没有成功会等待,等待时被中断,会抛出异常
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 释放锁
void unlock();
Condition newCondition();
那么问题来了,lock()方法获取锁失败等待时,被中断,会发生什么?
不会立即抛出异常,会存储中断状态值,获取锁之后再抛出异常
ReentrantLock
公平锁与非公平锁
公平锁:
按照请求锁的顺序分配,稳定的获得锁的机会,但是性能比非公平锁低;
非公平锁:
不按照请求锁的顺序分配,抢占式的获取锁。
ReentrantLock 默认非公平锁
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
�三个重要内部类
/**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {...}
// Sync object for non-fair locks
static final class NonfairSync extends Sync {...}
// Sync object for fair locks
static final class FairSync extends Sync{...}
class Sync
class NonfairSync
lock() 获取锁,如果未获取到,则等待
tryAcquire() 获取锁,立即返回结果
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
// 不过前面有没有线程,直接cas尝试获取锁,(非公平),但只有一次机会
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 调用的是AQS的父类方法,又变回了公平锁
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
// 调用的是 父类Sync 的 方法
return nonfairTryAcquire(acquires);
}
}
class FairSync
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
// 直接调用父类AQS的acquire方法
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果锁空闲,且队列中没有线程排在当前线程之前,就当前线程尝试获取锁;
// 如果尝试成功,就让当前线程获取锁;如果失败,就让线程节点入队(继续执行AQS的acquire中的逻辑)
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 这里是可重入锁的逻辑。
// 如果当前线程就是持有锁的线程,则累加更新 state
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
ReentrantLock的一些方法
无论是公平锁还是非公平锁,tryLock调用的都是用的Sync类中的 nonfairTryAcquire()方法
/**
* Acquires the lock only if it is not held by another thread at the time
* of invocation.
*
* <p>Acquires the lock if it is not held by another thread and
* returns immediately with the value {@code true}, setting the
* lock hold count to one. Even when this lock has been set to use a
* fair ordering policy, a call to {@code tryLock()} <em>will</em>
* immediately acquire the lock if it is available, whether or not
* other threads are currently waiting for the lock.
* This "barging" behavior can be useful in certain
* circumstances, even though it breaks fairness. If you want to honor
* the fairness setting for this lock, then use
* {@link #tryLock(long, TimeUnit) tryLock(0, TimeUnit.SECONDS) }
* which is almost equivalent (it also detects interruption).
*
* <p>If the current thread already holds this lock then the hold
* count is incremented by one and the method returns {@code true}.
*
* <p>If the lock is held by another thread then this method will return
* immediately with the value {@code false}.
*
* @return {@code true} if the lock was free and was acquired by the
* current thread, or the lock was already held by the current
* thread; and {@code false} otherwise
*/
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
ReentrantLock和Synchronized的区别
- ReentrantLock和Synchronized 最核心的区别就在于 Synchronized适合于并发竞争低的情况,因为Synchronized的锁升级如果最终升级为重量级锁在使用的过程中是没有办法消除的,意味着每次都要和cpu去请求锁资源,而ReentrantLock主要是提供了阻塞的能力, 通过在高并发下线程的挂起,来减少竞争,提高并发能力 ,所以我们文章标题的答案,也就显而易见了。
- synchronized是一个关键字,是由 jvm层面 去实现的,而ReentrantLock是由 java api 去实现的。
- synchronized是隐式锁,可以 自动释放锁 ,ReentrantLock是显式锁,需要 手动释放锁 。
- ReentrantLock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去, 不能够响应中断。
- ReentrantLock可以获取锁状态,而synchronized不能。