1. 背景介绍
都知道synchronize是通过markword相关机制实现同步, 众所周知java中还有另一种同步机制, 就是通过Lock相关类来实现, 今天我们就以ReentrantLock来探索一下java对Lock的具体实现方式
2. 源码追踪
2.1 ReentrantLock的公平和非公平锁源码分析
ReentrantLock内部实现了两种锁机制, 一种是公平锁, 一种是非公平锁. 当调用无参构造器时, 采用的是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
- 公平锁(FairSync): 通过引入排队机制, 实现先申请锁的, 先获得锁; 后申请锁的, 后获取锁
static final class FairSync extends Sync {
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 这里会先判断在队列中,当前线程节点是否排在第一个
// 如果是,则通过cas尝试去获取锁
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;
}
}
- 非公平锁(NonfairSync): 先申请锁的不一定先获得锁,获取锁的新线程和排队中的线程竞争,谁抢到就是谁的. 需要注意的是,非公平锁内部也有排队,也就是说如果有一个线程尝试获取已经被占用的锁,则这个线程也会加入排队
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
// 可以看到,此处没有排队机制,谁先抢到就是谁的
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
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;
}
2.1 ReentrantLock非公平锁实现下, 获取锁/释放锁的过程.(公平锁过程类似)
2.1.1 流程图
2.1.2. 获取锁/释放锁的关键代码分析
- 获取锁的关键代码
// java.util.concurrent.locks.AbstractQueuedSynchronizer.java
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) { // 判断当前线程是否排队在最前面, 并且cas获取锁成功
setHead(node);
p.next = null;
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) // 如果未获取到锁, 则调用LockSupport.park将线程挂起
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
- 释放锁的关键代码
public final boolean release(int arg) {
if (tryRelease(arg)) { // 首先尝试释放锁
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 释放锁成功是, 通过LockSupport.unpark将排在最前面的线程唤醒
return true;
}
return false;
}
3. LockSupport的进一步探究
// LockSupport.java
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread); // 通过Unsafe实现
}
public static void park() {
UNSAFE.park(false, 0L); // 通过UnsafeE实现
}
通过源码, 我们可以知道LockSupport的park和unpark都是通过Unsafe中对应的方式实现的,
接下来看看Unsafe类中相关的源码, 可以发现Unsafe中这个两个方法均是native方法, 也就是说是通过c/c++实现的, 我们进入c/c++中在一探究竟
// Unsafe.java
public native void unpark(Object var1);
public native void park(boolean var1, long var2);
4. Unsafe中park和unpark方法对应在hotspot中的c/c++实现
// hotspot\src\share\vm\prims\unsafe.cpp
// These are the methods for 1.8.0
static JNINativeMethod methods_18[] = {
{CC"park", CC"(ZJ)V", FN_PTR(Unsafe_Park)},
{CC"unpark", CC"("OBJ")V", FN_PTR(Unsafe_Unpark)}
}
- park通过c++的Unsafe_Park方法实现
UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time))
UnsafeWrapper("Unsafe_Park");
EventThreadPark event;
JavaThreadParkedState jtps(thread, time != 0);
thread->parker()->park(isAbsolute != 0, time); // 通过调用Park::park, 挂起当前线程
if (event.should_commit()) {
oop obj = thread->current_park_blocker();
event.set_klass((obj != NULL) ? obj->klass() : NULL);
event.set_timeout(time);
event.set_address((obj != NULL) ? (TYPE_ADDRESS) cast_from_oop<uintptr_t>(obj) : 0);
event.commit();
}
UNSAFE_END
进一步调用Parker的park方法, 这个依赖于具体的操作系统, 此处只专注linux操作系统下的实现, 核心源码如下:
// hotspot\src\os\linux\vm\os_linux.cpp
void Parker::park(bool isAbsolute, jlong time) {
// ...
if (time == 0) {
_cur_index = REL_INDEX;
// 通过调用操作系统的pthread_cond_wait方法, 实现无期限挂起等待
status = pthread_cond_wait (&_cond[_cur_index], _mutex) ;
} else {
_cur_index = isAbsolute ? ABS_INDEX : REL_INDEX;
// 通过操作系统的pthread_cond_timedwait方法, 实现会超时的等待
status = os::Linux::safe_cond_timedwait (&_cond[_cur_index], _mutex, &absTime) ;
if (status != 0 && WorkAroundNPTLTimedWaitHang) {
pthread_cond_destroy (&_cond[_cur_index]) ;
pthread_cond_init (&_cond[_cur_index], isAbsolute ? NULL : os::Linux::condAttr());
}
}
// ...
}
- unpark通过Unsafe_Unpark方法实现
// hotspot\src\os\linux\vm\os_linux.cpp
UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread))
UnsafeWrapper("Unsafe_Unpark");
Parker* p = NULL;
if (jthread != NULL) {
oop java_thread = JNIHandles::resolve_non_null(jthread);
if (java_thread != NULL) {
jlong lp = java_lang_Thread::park_event(java_thread);
if (lp != 0) {
p = (Parker*)addr_from_java(lp);
} else {
MutexLocker mu(Threads_lock);
java_thread = JNIHandles::resolve_non_null(jthread);
if (java_thread != NULL) {
JavaThread* thr = java_lang_Thread::thread(java_thread);
if (thr != NULL) {
p = thr->parker();
if (p != NULL) { // Bind to Java thread for next time.
java_lang_Thread::set_park_event(java_thread, addr_to_java(p));
}
}
}
}
}
}
if (p != NULL) {
#ifndef USDT2
HS_DTRACE_PROBE1(hotspot, thread__unpark, p);
#else /* USDT2 */
HOTSPOT_THREAD_UNPARK(
(uintptr_t) p);
#endif /* USDT2 */
p->unpark(); // 调用Parker::unpark方法实现unpark
}
UNSAFE_END
看看Parker的unpark方法具体实现
// hotspot\src\os\linux\vm\os_linux.cpp
void Parker::unpark() {
int s, status ;
status = pthread_mutex_lock(_mutex);
assert (status == 0, "invariant") ;
s = _counter;
_counter = 1;
if (s < 1) {
if (_cur_index != -1) {
// thread is definitely parked
if (WorkAroundNPTLTimedWaitHang) {
// 通过操作系统的pthread_cond_signal方法唤醒挂起的线
status = pthread_cond_signal (&_cond[_cur_index]); 程
assert (status == 0, "invariant");
status = pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant");
} else {
status = pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant");
// 通过操作系统的pthread_cond_signal方法唤醒挂起的线
status = pthread_cond_signal (&_cond[_cur_index]);
assert (status == 0, "invariant");
}
} else {
pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant") ;
}
} else {
pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant") ;
}
}
5. 总结
- java中Lock类优先通过cas方式获取锁
- 如果获取锁失败, 通过LockSupport.park挂起线程, 相应的, 通过LockSupport.unpark唤醒挂起的线程
- LockSupport.park底层实现是通过操作系统的pthread_cond_wait(不会超时)和pthread_cond_timedwait(会超时)方法实现
- LockSupport.unpark底层通过pthread_cond_signal实现线程唤醒