LockSupport 和 CAS 是Java并发包中很多并发工具实现并发操作的基础,它们底层其实都是依赖Unsafe类实现,下面看下其源码
private LockSupport() {} // Cannot be instantiated.
// Hotspot implementation via intrinsics API
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long parkBlockerOffset;
static {
try {
parkBlockerOffset = unsafe.objectFieldOffset
(java.lang.Thread.class.getDeclaredField("parkBlocker"));
} catch (Exception ex) { throw new Error(ex); }
}
首先,LockSupport类是不能够被用户程序实例化的,该类有两个成员变量,其中unsafe是java提供的底层操作类,对于parkBlockerOffset,还记得Thread类有个成员叫parkBlocker吗,它表示线程阻塞在哪个对象上,这里取其相对与Thread的偏移地址,方便后续操作。那么这里有个问题,为什么不使用set,get方法进行访问呢?试想如果线程处于阻塞状态,此时要获取这个parkBlocker对象,使用set,get方法,线程是没有办法响应的!所以只能获取到地址,然后获取对象.接下来我们主要看下常用到的两个方法,park和unpark.
1,park方法
public static void park() {
unsafe.park(false, 0L);
}
unsafe的park是一个native方法,其源码在unsafe的Unsafe_Park方法中,主要是下面这一句:
thread->parker()->park(isAbsolute != 0, time);
isAbsolute就是上面传过来的false,这里我们暂时不知道这个参数是干啥的,继续看。还记得之前Thread源码阅读的时候,在jvm的Thread类中有个成员
// JSR166 per-thread parker
private:
Parker* _parker;
public:
Parker* parker() { return _parker; }
之前在阅读Object源码以及Thread源码时都提到用到了park,当时没有仔细看这里的park是如何实现的,下面好好看下这个Parker类,每个线程都有对应的一个Parker实例,其定义如下:
class Parker : public os::PlatformParker {
private:
volatile int _counter ;
Parker * FreeNext ;
JavaThread * AssociatedWith ; // Current association
public:
Parker() : PlatformParker() {
_counter = 0 ;
FreeNext = NULL ;
AssociatedWith = NULL ;
}
protected:
~Parker() { ShouldNotReachHere(); }
public:
// For simplicity of interface with Java, all forms of park (indefinite,
// relative, and absolute) are multiplexed into one call.
void park(bool isAbsolute, jlong time);
void unpark();
// Lifecycle operators
static Parker * Allocate (JavaThread * t) ;
static void Release (Parker * e) ;
private:
static Parker * volatile FreeList ;
static volatile int ListLock ;
};
class PlatformParker : public CHeapObj<mtInternal> {
protected:
enum {
REL_INDEX = 0,
ABS_INDEX = 1
};
int _cur_index; // which cond is in use: -1, 0, 1
pthread_mutex_t _mutex [1] ;
pthread_cond_t _cond [2] ; // one for relative times and one for abs.
public: // TODO-FIXME: make dtor private
~PlatformParker() { guarantee (0, "invariant") ; }
public:
PlatformParker() {
int status;
status = pthread_cond_init (&_cond[REL_INDEX], os::Linux::condAttr());
assert_status(status == 0, status, "cond_init rel");
status = pthread_cond_init (&_cond[ABS_INDEX], NULL);
assert_status(status == 0, status, "cond_init abs");
status = pthread_mutex_init (_mutex, NULL);
assert_status(status == 0, status, "mutex_init");
_cur_index = -1; // mark as unused
}
};
可以看到Parker类继承自PlatformParker,父类中有两个成员互斥锁,和线程同步条件变量,在构造函数中进行初始化,可以看到Parker实际上是通过posix的mutex,condition实现的.再看下子类Parker,他实际上是一个链表,有成员_counter,这里我们暂时不知道这个_counter是干啥的,继续看,还有一个成员表示当前线程,还有个FreeList,和ListLock,这里我们暂时也不知道是干啥的.我们先来看下park方法干了些啥,
if (Atomic::xchg(0, &_counter) > 0) return;
这里xchg实际上是执行一个汇编指令xchg,作用是将_counter的值替换为0,如果此时_counter>0,那么park函数立即返回。哦,这个_counter就是LockSupport中注释里说的"permit",许可,park如果发现当前已经有许可,那么就立即返回.接着看,
Thread* thread = Thread::current();
assert(thread->is_Java_thread(), "Must be JavaThread");
JavaThread *jt = (JavaThread *)thread;
// Optional optimization -- avoid state transitions if there's an interrupt pending.
// Check interrupt before trying to wait
if (Thread::is_interrupted(thread, false)) {
return;
}
如果当前线程已经被中断,立即返回,
timespec absTime;
if (time < 0 || (isAbsolute && time == 0) ) { // don't wait at all
return;
}
if (time > 0) {
unpackTime(&absTime, isAbsolute, time);
}
这里我们知道了isAbsolute是什么意思了,即所谓的绝对时间,那什么是绝对时间?源码中注释如下:
/*
The passed in time value is either a relative time in nanoseconds or an absolute time in milliseconds.
*/
我理解这里所说的是精度问题,再往下看,
// Don't wait if cannot get lock since interference arises from
// unblocking. Also. check interrupt before trying wait
if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) {
return;
}
如果无法获取到锁,立即返回
if (_counter > 0) { // no wait needed
_counter = 0;
status = pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant") ;
// Paranoia to ensure our locked and lock-free paths interact
// correctly with each other and Java-level accesses.
OrderAccess::fence();
return;
}
如果此时_counter被修改了,值大于0,解锁,立即返回
OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
jt->set_suspend_equivalent();
// cleared by handle_special_suspend_equivalent_condition() or java_suspend_self()
assert(_cur_index == -1, "invariant");
if (time == 0) {
_cur_index = REL_INDEX; // arbitrary choice when not timed
status = pthread_cond_wait (&_cond[_cur_index], _mutex) ;
} else {
_cur_index = isAbsolute ? ABS_INDEX : REL_INDEX;
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());
}
}
_cur_index = -1;
首先设置osThread状态为CONDVAR_WAIT,如果time为0,则调用pthread_cond_wait一直等待条件被触发,否则调用pthread_cond_timedwait进行等待
_counter = 0 ;
status = pthread_mutex_unlock(_mutex) ;
等待结束后,设置_counter为0,释放锁,以上就是park的执行逻辑.
2,unpark方法
void Parker::unpark() {
int s, status ;
status = pthread_mutex_lock(_mutex);
assert (status == 0, "invariant") ;
s = _counter;
_counter = 1;
if (s < 1) {
// thread might be parked
if (_cur_index != -1) {
// thread is definitely parked
if (WorkAroundNPTLTimedWaitHang) {
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");
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") ;
}
}
首先加锁,因为要访问互斥量,然后设置_counter=1,如果_counter设置之前的值大于等于1,通知等待的条件变量,否则,直接释放锁,然后返回.
从上我们可以看到,park和unpark主要借助于linux线程相关的以下几个函数实现:
pthread_mutex_lock
pthread_mutex_unlock
pthread_cond_wait
pthread_cond_timedwait
pthread_cond_signal
阅读源码的过程中发现还有个类ParkEvent也提供了park/unpark功能,那他们有啥区别吗?源码中有这样一段注释
// The base-class, PlatformEvent, is platform-specific while the ParkEvent is
// platform-independent. PlatformEvent provides park(), unpark(), etc., and
// is abstract -- that is, a PlatformEvent should never be instantiated except
// as part of a ParkEvent.
// Equivalently we could have defined a platform-independent base-class that
// exported Allocate(), Release(), etc. The platform-specific class would extend
// that base-class, adding park(), unpark(), etc.
//
// A word of caution: The JVM uses 2 very similar constructs:
// 1. ParkEvent are used for Java-level "monitor" synchronization.
// 2. Parkers are used by JSR166-JUC park-unpark.
//
// We'll want to eventually merge these redundant facilities and use ParkEvent.
意思是说ParkEvent是用于java级别的synchronize关键字,Parker是JSR166来的并发工具集合,后面会统一使用ParkEvent,我们看下ParkEvent类,
class ParkEvent : public os::PlatformEvent {
private:
ParkEvent * FreeNext ;
// Current association
Thread * AssociatedWith ;
intptr_t RawThreadIdentity ; // LWPID etc
volatile int Incarnation ;
// diagnostic : keep track of last thread to wake this thread.
// this is useful for construction of dependency graphs.
void * LastWaker ;
public:
// MCS-CLH list linkage and Native Mutex/Monitor
ParkEvent * volatile ListNext ;
ParkEvent * volatile ListPrev ;
volatile intptr_t OnList ;
volatile int TState ;
volatile int Notified ; // for native monitor construct
volatile int IsWaiting ; // Enqueued on WaitSet
private:
static ParkEvent * volatile FreeList ;
static volatile int ListLock ;
// It's prudent to mark the dtor as "private"
// ensuring that it's not visible outside the package.
// Unfortunately gcc warns about such usage, so
// we revert to the less desirable "protected" visibility.
// The other compilers accept private dtors.
protected: // Ensure dtor is never invoked
~ParkEvent() { guarantee (0, "invariant") ; }
ParkEvent() : PlatformEvent() {
AssociatedWith = NULL ;
FreeNext = NULL ;
ListNext = NULL ;
ListPrev = NULL ;
OnList = 0 ;
TState = 0 ;
Notified = 0 ;
IsWaiting = 0 ;
}
// We use placement-new to force ParkEvent instances to be
// aligned on 256-byte address boundaries. This ensures that the least
// significant byte of a ParkEvent address is always 0.
void * operator new (size_t sz) ;
void operator delete (void * a) ;
public:
static ParkEvent * Allocate (Thread * t) ;
static void Release (ParkEvent * e) ;
} ;
它继承了PlatformEvent类
class PlatformEvent : public CHeapObj<mtInternal> {
private:
double CachePad [4] ; // increase odds that _mutex is sole occupant of cache line
volatile int _Event ;
volatile int _nParked ;
pthread_mutex_t _mutex [1] ;
pthread_cond_t _cond [1] ;
double PostPad [2] ;
Thread * _Assoc ;
public: // TODO-FIXME: make dtor private
~PlatformEvent() { guarantee (0, "invariant") ; }
public:
PlatformEvent() {
int status;
status = pthread_cond_init (_cond, os::Linux::condAttr());
assert_status(status == 0, status, "cond_init");
status = pthread_mutex_init (_mutex, NULL);
assert_status(status == 0, status, "mutex_init");
_Event = 0 ;
_nParked = 0 ;
_Assoc = NULL ;
}
// Use caution with reset() and fired() -- they may require MEMBARs
void reset() { _Event = 0 ; }
int fired() { return _Event; }
void park () ;
void unpark () ;
int TryPark () ;
int park (jlong millis) ; // relative timed-wait only
void SetAssociation (Thread * a) { _Assoc = a ; }
} ;
在PlatformEvent中定义了park,unpark方法,我们首先看下在parkEvent中park方法的实现,
int v ;
for (;;) {
v = _Event ;
if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ;
}
这里的_Event与上面的_counter类似,如果cmxchg成功,相当与是消耗掉一个许可,那么跳出循环,接着
if (v == 0) {
// Do this the hard way by blocking ...
int status = pthread_mutex_lock(_mutex);
assert_status(status == 0, status, "mutex_lock");
guarantee (_nParked == 0, "invariant") ;
++ _nParked ;
while (_Event < 0) {
status = pthread_cond_wait(_cond, _mutex);
// for some reason, under 2.7 lwp_cond_wait() may return ETIME ...
// Treat this the same as if the wait was interrupted
if (status == ETIME) { status = EINTR; }
assert_status(status == 0 || status == EINTR, status, "cond_wait");
}
-- _nParked ;
_Event = 0 ;
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "mutex_unlock");
// Paranoia to ensure our locked and lock-free paths interact
// correctly with each other.
OrderAccess::fence();
}
如果剩余许可大于0,直接返回,如果等于0,说明此时_Event<=-1,此时进入条件等待,再看下unpark
if (Atomic::xchg(1, &_Event) >= 0) return;
如果已有许可,返回,接着
int status = pthread_mutex_lock(_mutex);
assert_status(status == 0, status, "mutex_lock");
int AnyWaiters = _nParked;
assert(AnyWaiters == 0 || AnyWaiters == 1, "invariant");
if (AnyWaiters != 0 && WorkAroundNPTLTimedWaitHang) {
AnyWaiters = 0;
pthread_cond_signal(_cond);
}
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "mutex_unlock");
if (AnyWaiters != 0) {
status = pthread_cond_signal(_cond);
assert_status(status == 0, status, "cond_signal");
}
看下来,这两个类在实现上没有什么不同,但是为什么要两个类呢?再会过头看注释,ParkEvent是用于java级别的synchronize关键字,Parker是JSR166来的并发工具集合,难道是在使用上有不同?我们编写下测试例子:
public class TestSynchronized {
private static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
int N = 5;
List<Thread> threads = new ArrayList<Thread>();
for(int i = 0; i < N; ++i){
Thread t = new Thread("TestSynchronized-thread-"+i){
@Override
public void run() {
synchronized (lock){
System.out.println(Thread.currentThread().getName() + " get synchronized lock!");
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
threads.add(t);
}
synchronized(lock){
for(Thread t : threads){
t.start();
Thread.sleep(1000);
}
}
for(Thread t : threads)
t.join();
}
}
其结果如下:
TestSynchronized-thread-4 get synchronized lock!
TestSynchronized-thread-3 get synchronized lock!
TestSynchronized-thread-2 get synchronized lock!
TestSynchronized-thread-1 get synchronized lock!
TestSynchronized-thread-0 get synchronized lock!
额,最先启动的线程,最后获取到锁,是不是有点与认知不一样,这里的lock最终底层调用的是ParkEvent.park,再看个例子:
public class ReentrantLockTest {
public static void main(String[] args)throws InterruptedException{
final ReentrantLock lock = new ReentrantLock();
List<Thread> threadList = new ArrayList<Thread>();
int N = 5;
for(int i=0;i<N;i++){
Thread t = new Thread("ReentrantLockTest-thread-"+i){
@Override
public void run() {
try{
lock.lock();//底层park实现
System.out.println(Thread.currentThread().getName()+" test");
Thread.sleep(100);
}catch (Exception e){
}finally {
lock.unlock();
}
}
};
threadList.add(t);
}
lock.lock();
for(Thread t : threadList){
t.start();
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();//底层unpark实现
for(Thread t : threadList){
t.join();
}
}
}
执行结果如下:
ReentrantLockTest-thread-0 test
ReentrantLockTest-thread-1 test
ReentrantLockTest-thread-2 test
ReentrantLockTest-thread-3 test
ReentrantLockTest-thread-4 test
这里与正常的认知想符合,先启动先获得锁嘛,这里的lock底层是调用Parker.park实现,我们先来看下lock.lock()都干了啥,
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
这里如果没有获取到锁,会将当前线程封装成一个节点,加入到等待队列末尾,再看下synchrozied的等待对列,还记得Object源码阅读时wait方法中有一行
AddWaiter (&node) ;//添加到ObjectMonitor的等待队列中_Waitset
其函数如下
inline void ObjectMonitor::AddWaiter(ObjectWaiter* node) {
assert(node != NULL, "should not dequeue NULL node");
assert(node->_prev == NULL, "node already in list");
assert(node->_next == NULL, "node already in list");
// put node at end of queue (circular doubly linked list)
if (_WaitSet == NULL) {
_WaitSet = node;
node->_prev = node;
node->_next = node;
} else {
ObjectWaiter* head = _WaitSet ;
ObjectWaiter* tail = head->_prev;
assert(tail->_next == head, "invariant check");
tail->_next = node;
head->_prev = node;
node->_next = head;
node->_prev = tail;
}
}
看到问题了吧,这里每个等待节点进来不是像上面那样添加到队列末尾,而是添加到队列的头部,哦,这样就可以解释上面获取锁的顺序了!