java为我们提供了synchronized来支持在多线程环境下的同步控制,但是synchronized有很多缺陷在很多时候会造成严重的后果。例如死锁问题,长时间堵塞问题等。为此java并发包中提供了ReentrantLock可以实现synchronized不能实现的功能,例如可中断、超时获取,通过控制获取锁的事时间可以很好的避免长时间堵塞问题,也可以说是通过超时获取可以解决死锁问题,当获取锁超时失败了,放弃当前已经获取的锁资源,从而不会堵塞其他线程去获取锁资源。
公平锁和非公平锁
- 公平锁:等待锁资源的线程按照先来后到的顺序来获取后续释放的锁资源。
- 非公平锁:后续进来等待锁资源的线程可能会比先来等待锁资源的线程更先获取锁资源。
可重入锁
已经获取锁资源的线程可再次获取锁,释放锁的次数要和获取锁的次数相同。只有全部释放完成了之后才会真正的释放锁,其他线程才能去获取锁资源。
非公平锁
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {//非公平锁获取线程方式,尝试当前去线程获取锁
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
...
}
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() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);//AQS实现,内部调用了tryAcquire()
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
公平锁
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
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) {//严格按照FIFO顺序去获取锁资源
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&//如果当前线程是head的后继节点,则当前线程尝试获取锁资源
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
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可重入锁最关键的部分就是其设计思路,公平锁和非公平锁的设计。