java源码分析-Object类wait/notify

java源码分析-Object类wait/notify

​ 前面我们简单的把Object类的源码浏览了一遍,但是也只是简单的看了一下,没有深入学习。Object类作为java中所有类的基类,其重要性不言而喻。本片我们就来详细了解一下Object类中的wait方法和notify方法。

源码:

public final void wait() throws InterruptedException {
    wait(0);
}

public final native void wait(long timeout) throws InterruptedException;

public final void wait(long timeout, int nanos) throws InterruptedException {
    if (timeout < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
            "nanosecond timeout value out of range");
    }

    if (nanos > 0) {
        timeout++;
    }

    wait(timeout);
}

Object类中其实定义了三个wait方法,不做底层最终都是调用wait(long timeout)方法来实现。

这个方法的作用就是让调用该方法的线程处于阻塞状态,当有其他线程调用notify/notifyAll方法,或者当wait方法的等待时间结束时,调用线程就会被唤醒结束阻塞状态,转为就绪状态,此时线程就可以正常的竞争cpu资源和锁(monitor)对象,当拿到锁时,就能执行线程方法了。

例子:

package test.java.lang;

public class ObjectDemo {
    public static void main(String[] args) throws InterruptedException {
        final Object obj = new Object();
//        obj.wait();
        Thread t0 = new Thread(new MyThread1(obj));
        t0.start();
        Thread.sleep(100);
        Thread t1 = new Thread(new MyThread2(obj));
        t1.start();
    }
}

class MyThread1 implements Runnable{
    private Object object;

    MyThread1(Object object){
        this.object = object;
    }

    @Override
    public void run() {
        sycMethod();
    }

    private void sycMethod() {
        System.out.println(Thread.currentThread().getName()+"-> before synchronized ");
        synchronized (object){
            System.out.println(Thread.currentThread().getName()+"-> start .");
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName()+"-> start wait ");
                object.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-> end .");
        }
        System.out.println(Thread.currentThread().getName()+"-> end synchronized ");
    }
}

class MyThread2 implements Runnable{
    private Object object;

    MyThread2(Object object){
        this.object = object;
    }

    @Override
    public void run() {
        sycMethod();
    }

    private void sycMethod() {
        System.out.println(Thread.currentThread().getName()+"-> before synchronized ");
        synchronized (object){
            System.out.println(Thread.currentThread().getName()+"-> start .");
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+"-> start notify ");
                object.notify();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-> end .");
        }
        System.out.println(Thread.currentThread().getName()+"-> end synchronized ");


    }
}

执行程序,结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rQQfEkO8-1612440634227)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210204171645789.png)]

从结果中,我们看到一些现象:

(1)Thread-0 进入sychronized后,thread-1会一直被阻塞;

(2)当Thread-0 调用wait方法后,thread-1才会进入到sychronized,同时Thread-0就被挂起了,不再执行后面流程;

(3)当thread-1调用notify方法后,Thread-0才重新执行后面流程。

这里我们就以一些问题了:

(1)wait方法为什么要在sychronized代码块中使用,不这样行不行?

(2)当thread-0调用wait方法后,为什么就不再执行后面的代码了?

(3)当thread-0调用wait方法后,为什么thread-1就可以执行了?

(4)的那个thread-1调用notify方法后,为什么thread-0就又可以执行了?

下面我们带着这些问题去看一下wait方法和notify方法的底层原理。

对了,在讲这两个方法的原理之前我们先在简单的了解一下线程同步关键字Synchronized的相关知识(这里只简单介绍,在学习多线程时,我们在详细分析):

​ JVM规范中可以看到Synchonized在JVM里的实现原理,JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,但两者的实现细节不一样。代码块同步是使用monitorenter 和monitorexit指令实现的,而方法同步是使用另外一种方式实现的,细节在JVM规范里并没有详细说明。但是,方法的同步同样可以使用这两个指令来实现。

如下面代码:

public static void main(String[] args) { 
	synchronized (args){
            System.out.println("1111");
        }
}

通过javap 命令生成的字节码中包含 monitorentermonitorexit 指令:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zHDs9tUS-1612440634229)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210204173650456.png)]

​ 当执行monitorenter指令时,线程试图获取锁也就是获取monitor的持有权。

​ 相应的在执行monitorexit指令后,将锁计数器设为0,表明锁被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外一个线程释放为止。

下面我们来带着问题分析wait方法和notify的底层原理。

1.wait方法的使用条件

​ 这个wait的使用是有条件的,必须在被锁的代码块中使用,也就是需要在sychonized的代码块中使用,否则就会抛出IllegalMonitorStateException异常,如下:

public class ObjectDemo { 
	public static void main(String[] args) throws InterruptedException {
         final Object obj = new Object();
         obj.wait();
 	}
}

执行上面代码:

在这里插入图片描述

那么问题来了,为什么Object的wait方法必须要放在synchronized块中使用呢?

其实在wait方法的注释上已经又说明了:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ElGGRxlV-1612440634232)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210204101716912.png)]

在wait方法的源码上已经注明了,The current thread must own this object's monitor.就是说wait方法的使用必须是该线程持有这个对象的monitor监视器,我们就理解为锁对象。当一个线程在没有获取到锁对象就调用wait方法时,就会抛出java.lang.IllegalMonitorStateException异常了。

到这里,我们基本知道了wait方法的使用是确实有这样的限制,但为什么会有这样的限制呢?想要彻底搞清楚,就必须要看本地方法wait(0)的底层实现了。咱也不是C/C++开发的,不懂啊。怎么办,不管了,硬着头皮找一找吧。

首先需要查看wait(0)的本地方法在哪里。我们说过Object类中有很多本地方法,这些本地方法都通过registerNatives();方法注册到本地方法表中,那就看一下怎么注册的。

查找思路:找到wait方法的实现,看在哪里有IllegalMonitorStateException相关异常

首先打开:src/share/native/java/lang/Object.c

static JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
                            methods, sizeof(methods)/sizeof(methods[0]));
}

发现wait方法对应的c++中的方法是JVM_MonitorWait,那就好找了。在vs中打开hotspot源码找到这个JVM_MonitorWait方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-atFKCy3W-1612440634234)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210204103029117.png)]

src\share\vm\prims\jvm.cpp

JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
  JVMWrapper("JVM_MonitorWait");
  Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
  JavaThreadInObjectWaitState jtiows(thread, ms != 0);
  if (JvmtiExport::should_post_monitor_wait()) {
    JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms);

    // The current thread already owns the monitor and it has not yet
    // been added to the wait queue so the current thread cannot be
    // made the successor. This means that the JVMTI_EVENT_MONITOR_WAIT
    // event handler cannot accidentally consume an unpark() meant for
    // the ParkEvent associated with this ObjectMonitor.
  }
  ObjectSynchronizer::wait(obj, ms, CHECK);//调用 ObjectSynchronizer::wait
JVM_END

可以发现调用了ObjectSynchronizer::wait(obj, ms, CHECK);方法,继续跟进:

src\share\vm\runtime\synchronizer.cpp

// NOTE: must use heavy weight monitor to handle wait()	//这一句注释
void ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
  if (UseBiasedLocking) {
    BiasedLocking::revoke_and_rebias(obj, false, THREAD);
    assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
  }
  if (millis < 0) {
    TEVENT (wait - throw IAX) ;
    THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
  }
  ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj());
  DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), THREAD, millis);
  monitor->wait(millis, true, THREAD);//看这一句

  /* This dummy call is in place to get around dtrace bug 6254741.  Once
     that's fixed we can uncomment the following line and remove the call */
  // DTRACE_MONITOR_PROBE(waited, monitor, obj(), THREAD);
  dtrace_waited_probe(monitor, obj, THREAD);
}

还是没有找到我们想要的东西,但是我们看到注释中有说明,必须monitor去执行wait方法,说明我们这样找思路没错,往下看,ObjectSynchronizer::wait最后又依赖monitor->wait,点进去

src\share\vm\runtime\objectMonitor.cpp

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nEBv6sHT-1612440634235)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210204103605235.png)]

代码太长,我们截取一部分,在代码中我们看到了一句:

  // Throw IMSX or IEX.
  CHECK_OWNER();

好像很想我们要找的IllegalMonitorStateException缩写啊,好激动,点进去:

#define CHECK_OWNER()                                                             \
  do {                                                                            \
    if (THREAD != _owner) {                                                       \
      if (THREAD->is_lock_owned((address) _owner)) {                              \
        _owner = THREAD ;  /* Convert from basiclock addr to Thread addr */       \
        _recursions = 0;                                                          \
        OwnerIsThread = 1 ;                                                       \
      } else {                                                                    \
        TEVENT (Throw IMSX) ;                                                     \
        THROW(vmSymbols::java_lang_IllegalMonitorStateException());               \	//java_lang_IllegalMonitorStateException在这里
      }                                                                           \
    }                                                                             \
  } while (false)

果然,这段就校验wait方法是否含有monitor。之前我们就说过JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,那么这个monitor是什么呢、在HotSpot虚拟机中,monitor采用ObjectMonitor实现,可以看一下它的源码:

src\share\vm\runtime\objectMonitor.hpp

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QIMKU2RQ-1612440634237)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210204111017175.png)]

上图是objectMonitor定义,其中我们需要关注红色框标注的三个部分。

_owner:指向拥有当前monitor的线程;

_WaitSet:处于wait状态的线程,会被加入到wait set;

_EntryList:处于等待锁block状态的线程,会被加入到entry set;

_WaitSet和_EntryList是两个双向链表,结构定义如下图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xF8iOFrL-1612440634239)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210204105215187.png)]

整理一下粗略的逻辑:

​ _owner字段用于指向当前monitor的线程,而线程想要获取monitor对象,就需要通过调用monitorenter指令,而这个指令实际上就是我们java代码中Synchronized代码块来完成。即进入Synchronized代码块,就会调用monitorenter指令,线程获取到monitor对象,结束Synchronized代码块时,会调用monitorexit指令,也就会释放monitoer对象,_owner字段指向其他获取到锁的线程。

到这里我们就基本可以了解jvm的锁机制了。我们再回到最初的问题,为什么Object的wait方法必须要放在synchronized块中使用呢?也就是我们再来解读CHECK_OWNER()这段代码的大致意思。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rYEG29Ty-1612440634239)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210204161358263.png)]

(1)判断当前线程是否是当前monitor的所有者,当我们不是在Synchronized代码块中时,_owner不是我们当前线程,所以进入方法体;

(2)将_owner从一个BasicLock指针转换为一个线程地址,实际上就是当前线程获取到monitor对象,显然也是不行的,无法调用monitorenter指令;

(3)当上面两个条件都不满足时,抛出java_lang_IllegalMonitorStateException异常。

​ 到此,我们通过找到源码终于论证了Object的wait方法必须要放在synchronized块中使用!(这里我们只是简单的说明了JVM的同步锁Synchronized机制,后期会再详细分析)

2.wait方法的原理

​ 通过第一个问题对源代码的追踪和分析,我们大致能了解到wait方法的大致执行流程,而wait方法的具体实现就是在ObjectMonitor::wait() 函数中,函数源码如下:

void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
   Thread * const Self = THREAD ;
   assert(Self->is_Java_thread(), "Must be Java thread!");
   JavaThread *jt = (JavaThread *)THREAD;
 
   DeferredInitialize () ;
 
   // Throw IMSX or IEX.
   CHECK_OWNER();
 
   EventJavaMonitorWait event;
 
   // check for a pending interrupt
   if (interruptible && Thread::is_interrupted(Self, true) && !HAS_PENDING_EXCEPTION) {
     // post monitor waited event.  Note that this is past-tense, we are done waiting.
     if (JvmtiExport::should_post_monitor_waited()) {
        // Note: 'false' parameter is passed here because the
        // wait was not timed out due to thread interrupt.
        JvmtiExport::post_monitor_waited(jt, this, false);
     }
     if (event.should_commit()) {
       post_monitor_wait_event(&event, 0, millis, false);
     }
     TEVENT (Wait - Throw IEX) ;
     THROW(vmSymbols::java_lang_InterruptedException());
     return ;
   }
 
   TEVENT (Wait) ;
 
   assert (Self->_Stalled == 0, "invariant") ;
   Self->_Stalled = intptr_t(this) ;
   jt->set_current_waiting_monitor(this);
 
   // create a node to be put into the queue
   // Critically, after we reset() the event but prior to park(), we must check
   // for a pending interrupt.
   ObjectWaiter node(Self);//(1)
   node.TState = ObjectWaiter::TS_WAIT ;
   Self->_ParkEvent->reset() ;
   OrderAccess::fence();          // ST into Event; membar ; LD interrupted-flag
 
   // Enter the waiting queue, which is a circular doubly linked list in this case
   // but it could be a priority queue or any data structure.
   // _WaitSetLock protects the wait queue.  Normally the wait queue is accessed only
   // by the the owner of the monitor *except* in the case where park()
   // returns because of a timeout of interrupt.  Contention is exceptionally rare
   // so we use a simple spin-lock instead of a heavier-weight blocking lock.
 
   Thread::SpinAcquire (&_WaitSetLock, "WaitSet - add") ;
   AddWaiter (&node) ; //(2)
   Thread::SpinRelease (&_WaitSetLock) ;
 
   if ((SyncFlags & 4) == 0) {
      _Responsible = NULL ;
   }
   intptr_t save = _recursions; // record the old recursion count
   _waiters++;                  // increment the number of waiters
   _recursions = 0;             // set the recursion level to be 1
   exit (true, Self) ;                    // exit the monitor
   guarantee (_owner != Self, "invariant") ;
 
   // As soon as the ObjectMonitor's ownership is dropped in the exit()
   // call above, another thread can enter() the ObjectMonitor, do the
   // notify(), and exit() the ObjectMonitor. If the other thread's
   // exit() call chooses this thread as the successor and the unpark()
   // call happens to occur while this thread is posting a
   // MONITOR_CONTENDED_EXIT event, then we run the risk of the event
   // handler using RawMonitors and consuming the unpark().
   //
   // To avoid the problem, we re-post the event. This does no harm
   // even if the original unpark() was not consumed because we are the
   // chosen successor for this monitor.
   if (node._notified != 0 && _succ == Self) {
      node._event->unpark();
   }
 
   // The thread is on the WaitSet list - now park() it.
   // On MP systems it's conceivable that a brief spin before we park
   // could be profitable.
   //
   // TODO-FIXME: change the following logic to a loop of the form
   //   while (!timeout && !interrupted && _notified == 0) park()
 
   int ret = OS_OK ;
   int WasNotified = 0 ;
   { // State transition wrappers
     OSThread* osthread = Self->osthread();
     OSThreadWaitState osts(osthread, true);
     {
       ThreadBlockInVM tbivm(jt);
       // Thread is in thread_blocked state and oop access is unsafe.
       jt->set_suspend_equivalent();
 
       if (interruptible && (Thread::is_interrupted(THREAD, false) || HAS_PENDING_EXCEPTION)) {
           // Intentionally empty
       } else
       if (node._notified == 0) {
         if (millis <= 0) {
            Self->_ParkEvent->park () ;  //(3)
         } else {
            ret = Self->_ParkEvent->park (millis) ; //(4)
         }
       }
 
       // were we externally suspended while we were waiting?
       if (ExitSuspendEquivalent (jt)) {
          // TODO-FIXME: add -- if succ == Self then succ = null.
          jt->java_suspend_self();
       }
 
     } // Exit thread safepoint: transition _thread_blocked -> _thread_in_vm
 
 
     // Node may be on the WaitSet, the EntryList (or cxq), or in transition
     // from the WaitSet to the EntryList.
     // See if we need to remove Node from the WaitSet.
     // We use double-checked locking to avoid grabbing _WaitSetLock
     // if the thread is not on the wait queue.
     //
     // Note that we don't need a fence before the fetch of TState.
     // In the worst case we'll fetch a old-stale value of TS_WAIT previously
     // written by the is thread. (perhaps the fetch might even be satisfied
     // by a look-aside into the processor's own store buffer, although given
     // the length of the code path between the prior ST and this load that's
     // highly unlikely).  If the following LD fetches a stale TS_WAIT value
     // then we'll acquire the lock and then re-fetch a fresh TState value.
     // That is, we fail toward safety.
 
     if (node.TState == ObjectWaiter::TS_WAIT) {
         Thread::SpinAcquire (&_WaitSetLock, "WaitSet - unlink") ;
         if (node.TState == ObjectWaiter::TS_WAIT) {
            DequeueSpecificWaiter (&node) ;       // unlink from WaitSet
            assert(node._notified == 0, "invariant");
            node.TState = ObjectWaiter::TS_RUN ;
         }
         Thread::SpinRelease (&_WaitSetLock) ;
     }
 
     // The thread is now either on off-list (TS_RUN),
     // on the EntryList (TS_ENTER), or on the cxq (TS_CXQ).
     // The Node's TState variable is stable from the perspective of this thread.
     // No other threads will asynchronously modify TState.
     guarantee (node.TState != ObjectWaiter::TS_WAIT, "invariant") ;
     OrderAccess::loadload() ;
     if (_succ == Self) _succ = NULL ;
     WasNotified = node._notified ;
 
     // Reentry phase -- reacquire the monitor.
     // re-enter contended monitor after object.wait().
     // retain OBJECT_WAIT state until re-enter successfully completes
     // Thread state is thread_in_vm and oop access is again safe,
     // although the raw address of the object may have changed.
     // (Don't cache naked oops over safepoints, of course).
 
     // post monitor waited event. Note that this is past-tense, we are done waiting.
     if (JvmtiExport::should_post_monitor_waited()) {
       JvmtiExport::post_monitor_waited(jt, this, ret == OS_TIMEOUT);
     }
 
     if (event.should_commit()) {
       post_monitor_wait_event(&event, node._notifier_tid, millis, ret == OS_TIMEOUT);
     }
 
     OrderAccess::fence() ;
 
     assert (Self->_Stalled != 0, "invariant") ;
     Self->_Stalled = 0 ;
 
     assert (_owner != Self, "invariant") ;
     ObjectWaiter::TStates v = node.TState ;
     if (v == ObjectWaiter::TS_RUN) {
         enter (Self) ;
     } else {
         guarantee (v == ObjectWaiter::TS_ENTER || v == ObjectWaiter::TS_CXQ, "invariant") ;
         ReenterI (Self, &node) ;
         node.wait_reenter_end(this);
     }
 
     // Self has reacquired the lock.
     // Lifecycle - the node representing Self must not appear on any queues.
     // Node is about to go out-of-scope, but even if it were immortal we wouldn't
     // want residual elements associated with this thread left on any lists.
     guarantee (node.TState == ObjectWaiter::TS_RUN, "invariant") ;
     assert    (_owner == Self, "invariant") ;
     assert    (_succ != Self , "invariant") ;
   } // OSThreadWaitState()
 
   jt->set_current_waiting_monitor(NULL);
 
   guarantee (_recursions == 0, "invariant") ;
   _recursions = save;     // restore the old recursion count
   _waiters--;             // decrement the number of waiters
 
   // Verify a few postconditions
   assert (_owner == Self       , "invariant") ;
   assert (_succ  != Self       , "invariant") ;
   assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ;
 
   if (SyncFlags & 32) {
      OrderAccess::fence() ;
   }
 
   // check if the notification happened
   if (!WasNotified) {
     // no, it could be timeout or Thread.interrupt() or both
     // check for interrupt event, otherwise it is timeout
     if (interruptible && Thread::is_interrupted(Self, true) && !HAS_PENDING_EXCEPTION) {
       TEVENT (Wait - throw IEX from epilog) ;
       THROW(vmSymbols::java_lang_InterruptedException());
     }
   }
 
   // NOTE: Spurious wake up will be consider as timeout.
   // Monitor notify has precedence over thread interrupt.
}

这段函数相当的长,具体细节也不能说的很清楚,我们就大致了解一下其中比较重要的步骤:

(1)ObjectWaiter node(Self): Self 是Thread 对象,将当前线程封装成ObjectWaiter对象node;

(2)ObjectMonitor::AddWaiter() :将 node加入到 ObjectWaiter 的_WaitSet 中,那么当前线程就处于waiting状态了。

(3)exit (true, Self) :// exit the monitor 线程退出monitor,也就是说会与_owner字段解绑;

(4)Self->_ParkEvent->park () : 最终底层的park方法挂起线程;

看到这我们是不是就明白了我们文章开头的问题(2)、(3)了。

(2)当thread-0调用wait方法后,为什么就不再执行后面的代码了?

​ 当thread-0调用wait方法后,线程会被放入monitor的waitset队列中,处于waiting状态,最终会被挂起。

(3)当thread-0调用wait方法后,为什么thread-1就可以执行了?

​ 当thread-0调用wait方法后,monitor字段owner字段会重新指向其他想要获取锁对象的线程,也就是thread-1。当thread-1获取到monitor对象时,他就可以正常执行同步代码块了。

3.notify的原理

​ 分析流程和上面一样,只不过本地方法变为JVM_MonitorNotify。

src\share\vm\prims\jvm.cpp

JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, jobject handle))
  JVMWrapper("JVM_MonitorNotify");
  Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
  ObjectSynchronizer::notify(obj, CHECK);
JVM_END

src\share\vm\runtime\synchronizer.cpp

void ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
  if (UseBiasedLocking) {
    BiasedLocking::revoke_and_rebias(obj, false, THREAD);
    assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
  }
  if (millis < 0) {
    TEVENT (wait - throw IAX) ;
    THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
  }
  ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj());
  DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), THREAD, millis);
  monitor->wait(millis, true, THREAD);

  /* This dummy call is in place to get around dtrace bug 6254741.  Once
     that's fixed we can uncomment the following line and remove the call */
  // DTRACE_MONITOR_PROBE(waited, monitor, obj(), THREAD);
  dtrace_waited_probe(monitor, obj, THREAD);
}

src\share\vm\runtime\objectMonitor.cpp

// Consider:
// If the lock is cool (cxq == null && succ == null) and we're on an MP system
// then instead of transferring a thread from the WaitSet to the EntryList
// we might just dequeue a thread from the WaitSet and directly unpark() it.
//我们可能仅将一个线程从WaitSet取出,并直接调用unpark()方法
 
void ObjectMonitor::notify(TRAPS) {
  CHECK_OWNER();
  if (_WaitSet == NULL) {
     TEVENT (Empty-Notify) ;
     return ;
  }
  DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);
 
  int Policy = Knob_MoveNotifyee ;
 
  Thread::SpinAcquire (&_WaitSetLock, "WaitSet - notify") ;
  ObjectWaiter * iterator = DequeueWaiter() ; //封装成ObjectWaiter对象node
  if (iterator != NULL) {
     TEVENT (Notify1 - Transfer) ;
     guarantee (iterator->TState == ObjectWaiter::TS_WAIT, "invariant") ;
     guarantee (iterator->_notified == 0, "invariant") ;
     if (Policy != 4) {
        iterator->TState = ObjectWaiter::TS_ENTER ;
     }
     iterator->_notified = 1 ;
     Thread * Self = THREAD;
     iterator->_notifier_tid = Self->osthread()->thread_id();
 
     ObjectWaiter * List = _EntryList ;
     if (List != NULL) {
        assert (List->_prev == NULL, "invariant") ;
        assert (List->TState == ObjectWaiter::TS_ENTER, "invariant") ;
        assert (List != iterator, "invariant") ;
     }
 
     if (Policy == 0) {       // prepend to EntryList
         if (List == NULL) {
             iterator->_next = iterator->_prev = NULL ;
             _EntryList = iterator ;
         } else {
             List->_prev = iterator ;
             iterator->_next = List ;//链表头插入法插入entryList
             iterator->_prev = NULL ;
             _EntryList = iterator ;
        }
     } else
     if (Policy == 1) {      // append to EntryList
         if (List == NULL) {
             iterator->_next = iterator->_prev = NULL ;
             _EntryList = iterator ;
         } else {
            // CONSIDER:  finding the tail currently requires a linear-time walk of
            // the EntryList.  We can make tail access constant-time by converting to
            // a CDLL instead of using our current DLL.
            ObjectWaiter * Tail ;
            for (Tail = List ; Tail->_next != NULL ; Tail = Tail->_next) ;
            assert (Tail != NULL && Tail->_next == NULL, "invariant") ;
            Tail->_next = iterator ;  //链表尾插入法插入entryList
            iterator->_prev = Tail ;
            iterator->_next = NULL ;
        }
     } else
     if (Policy == 2) {      // prepend to cxq
         // prepend to cxq
         if (List == NULL) {
             iterator->_next = iterator->_prev = NULL ;
             _EntryList = iterator ;
         } else {
            iterator->TState = ObjectWaiter::TS_CXQ ;
            for (;;) {
                ObjectWaiter * Front = _cxq ;
                iterator->_next = Front ;  //头插入法插入cxq
                if (Atomic::cmpxchg_ptr (iterator, &_cxq, Front) == Front) {
                    break ;
                }
            }
         }
     } else
     if (Policy == 3) {      // append to cxq
        iterator->TState = ObjectWaiter::TS_CXQ ;
        for (;;) {
            ObjectWaiter * Tail ;
            Tail = _cxq ;
            if (Tail == NULL) {
                iterator->_next = NULL ;
                if (Atomic::cmpxchg_ptr (iterator, &_cxq, NULL) == NULL) {
                   break ;
                }
            } else {
                while (Tail->_next != NULL) Tail = Tail->_next ;
                Tail->_next = iterator ;  //尾插入法插入cxq
                iterator->_prev = Tail ;
                iterator->_next = NULL ;
                break ;
            }
        }
     } else {
        ParkEvent * ev = iterator->_event ;
        iterator->TState = ObjectWaiter::TS_RUN ;
        OrderAccess::fence() ;
        ev->unpark() ;  //底层调用unpark方法,唤醒阻塞的线程
     }
 
     if (Policy < 4) {
       iterator->wait_reenter_begin(this);
     }
 
     // _WaitSetLock protects the wait queue, not the EntryList.  We could
     // move the add-to-EntryList operation, above, outside the critical section
     // protected by _WaitSetLock.  In practice that's not useful.  With the
     // exception of  wait() timeouts and interrupts the monitor owner
     // is the only thread that grabs _WaitSetLock.  There's almost no contention
     // on _WaitSetLock so it's not profitable to reduce the length of the
     // critical section.
  }
 
  Thread::SpinRelease (&_WaitSetLock) ;
 
  if (iterator != NULL && ObjectMonitor::_sync_Notifications != NULL) {
     ObjectMonitor::_sync_Notifications->inc() ;
  }
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值