synchronized的同步原理:基于JVM机制,底层用到了Monitor锁
当竞争激烈的时候会升级成为重量级锁,效率会变得非常低。
ReentrantLock
ReentrantLock被作为 Java 语言中synchronized功能的替代类,它具有相同的内存语义、相同的锁定,但在大量争用条件下却有更好的性能,此外,它还有 synchronized关键字没有提供的其他特性;ReentrantLock底层是基于AQS(AbstractQueuedSynchronizer)类。
-
什么是可重入?
简单来说,它有一个与锁有关的获取计数器,如果拥有锁的某个线程再次获取锁,那么获取锁的计数器就+1,然后所需要被释放2次才能真正释放。
AQS当中的队列示意图:
-
当并发量很小的时候,使用synchronized关键字的效率比Lock的效率高一点,而当并发量很高的时候,Lock的性能就会比synchronized关键字高,具体的原因是因为synchronized关键字当竞争激烈的时候就会升级为重量级锁,而重量级锁的效率会变得非常的低下。
-
对于Lock的使用通过lock.lock()方法获取锁,在finally中通过lock.unlock()保证释放锁,如果忘记释放锁,那么这个锁就会一直占用着导致其它线程无法进入执行甚至还会造成死锁导致程序崩溃。
对于ReentrantLock需要了解了解一个概念公平锁和非公平锁:
- 公平锁:当同步队列中首节点释放同步状态后,先进先出队列首先获取同步状态的为其后继节点,如果不存在后继节点就获取等待时间最长的正常状态的线程。并发很高的情况下其效率比非公平锁要低。
原理:在通过CAS设置同步状态之前,会先去同步队列中查询是否存在比当前线程的等待时间长的,如果存在就不去改变该线程的状态,如果不存在就进行同步状态的改变。 - 非公平锁(默认):在释放同步状态以后所有的线程都会进入竞争同步状态,而获取同步状态的线程时随机的不确定的,有可能是等待时间最长的,也有可能是等待时间最短的。它不需要去同步队列中去与其它的节点进行比较,随便谁获取到同步状态。
公平锁核心源码:
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;
}
}
非公平锁核心源码:
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);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
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;
}
}
总结:
可以看到,公平锁比非公平锁多一个hasQueuedPredecessors()方法,即多一个去同步队列中与其他节点比较的过程。
锁的释放:
public void unlock() {
sync.release(1);
}
protected final boolean tryRelease(int releases) {
//1,同步状态值-1
int c = getState() - releases;
//2,判断当前释放同步状态的线程是否为获取同步状态的线程
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
//3,是否完全释放(因为是重入锁,必须等待所有的锁释放才算释放)
boolean free = false;
//4,代表完全释放
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
//5,设置状态
setState(c);
return free;
}