java中Lock相关类实现的探索(非synchronize)

1. 背景介绍

都知道synchronize是通过markword相关机制实现同步, 众所周知java中还有另一种同步机制, 就是通过Lock相关类来实现, 今天我们就以ReentrantLock来探索一下java对Lock的具体实现方式

2. 源码追踪

2.1 ReentrantLock的公平和非公平锁源码分析

ReentrantLock内部实现了两种锁机制, 一种是公平锁, 一种是非公平锁. 当调用无参构造器时, 采用的是非公平锁

   public ReentrantLock() {
       sync = new NonfairSync();
   }
  1. 公平锁(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;
        }
    }
  1. 非公平锁(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 流程图

java中Lock相关类实现的探索(非synchronize)_第1张图片

2.1.2. 获取锁/释放锁的关键代码分析

  1. 获取锁的关键代码
// 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);
    }
}
  1. 释放锁的关键代码
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. 总结

  1. java中Lock类优先通过cas方式获取锁
  2. 如果获取锁失败, 通过LockSupport.park挂起线程, 相应的, 通过LockSupport.unpark唤醒挂起的线程
  3. LockSupport.park底层实现是通过操作系统的pthread_cond_wait(不会超时)和pthread_cond_timedwait(会超时)方法实现
  4. LockSupport.unpark底层通过pthread_cond_signal实现线程唤醒

你可能感兴趣的:(java,c++,c语言)