我们经常会遇到一个问题,就是lock和synchronized有什么区别?
我们首先可以通过代码发现,lock是一个接口,而sychornized是一个修饰符
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
我们可以看到lock中主要有以上六个方法,那么接口肯定是需要实现的,而lock很重要的一个实现类就是我们的ReentrantLock类。
从名字来看,这是一个可重入的锁,这和synchronized是一样的,但是它的功能可远远不止它的名字所示。
我们可以看到这个类只有一个属性
private final Sync sync;
这个Sync是一个抽象类,那么意味着还有其他类来继承这个类
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;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
我们可以看到nonfairTryAcquire这个方法,按名字的意思是不公平的尝试获取锁,那么既然有不公平的获取,就应该有公平的获取锁的方式,所以我们的ReentrantLock是一个公平锁。
在nonfairTryAcquire中,首先调用了父类AQS中的getstate() 方法,获取当前的同步状态,如果为0,则表示当前资源空闲,于是采用CAS将资源的线程占有数+1。(这里没有管其他有没有等待的队列,直接CAS)
如果当前线程就是锁的拥有者,那么占有的资源数还是得加上,以方便后面释放锁的时候继续释放。否则的话直接返回false。
tryRelease方法也是类似的逻辑,不过是释放资源,且当state为0时就将锁的拥有者置空。
接下来就是两个实现类,分别是公平锁和非公平锁。
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);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
可以看到,我们这里使用lock是直接进行CAS,不会考虑等待队列,如果CAS失败,则直接进入AQS的acquire方法,且会调用tryAcquire方法再次进行非公平锁的尝试。
公平锁的实现就又是新的方式了
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) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
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;
}
}
公平锁的逻辑中tryAcquire判断的逻辑不一样了,这里考虑了等待的队列是否为空,以及当前线程在队列中的位置。
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}