美文网首页java jvm
Java多线程-线程状态

Java多线程-线程状态

作者: heyong | 来源:发表于2017-09-27 15:02 被阅读100次

    概述

    最近开始强迫自己养成学习做笔记的习惯,今天重新复习了一下JAVA线程的状态,记录一下自己学习到的东西

    JAVA线程状态

    根据自己的理解,绘画了一张图来描述线程的状态变化



    下面结合线程状态变化图进行简单讲解:

    • 调用start()方法,创建线程,等待获得CPU。
    • 执行同步块,多个线程竞争锁,没有获得锁的线程阻塞,等待锁的释放
    • 调用Thread.sleep() 方法,线程有限期等待(如果该线程之前获得锁,锁不会被释放)
    • 调用wait()方法,线程无限期等待,直到其他线程唤醒,当线程调用wait()方法时,线程进入一个阻塞队列,在后面会具体分析
    • 当线程退出run()方法,线程结束,关于线程的停止在下面的小节会详细讲解。

    深入wait()

    前面说调用wait()方法以后,线程会进入一个阻塞队列,接下来我们一步步剖析源码去了解。
    查看JDK源码,发现该方法在Object类中定义,是一个实例方法(Java中采用对象锁):

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

    wait()方法调用了wait(0),该方法是一个native方法:

    //The current thread must own this object's monitor.(调用该方法前,必须用于对象锁)
    public final native void wait(long timeout) throws InterruptedException;
    

    查看OpenJDK源码,openjdk\jdk\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},
    };
    

    可以看到wait 对应的是 native 方法是JVM_MonitorWait, 继续查看JVM_MonitorWait的实现:openjdk\hotspot\src\share\vm\prims\jvm.cpp

    VM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
      JVMWrapper("JVM_MonitorWait");
      Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
      assert(obj->is_instance() || obj->is_array(), "JVM_MonitorWait must apply to an object");
      JavaThreadInObjectWaitState jtiows(thread, ms != 0);
      if (JvmtiExport::should_post_monitor_wait()) {
        JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms);
      }
      ObjectSynchronizer::wait(obj, ms, THREAD);
    

    在该方法中调用了 ObjectSynchronizer::wait(obj, ms, THREAD)方法,具体实现如下:

    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");
      }
      //如果wait time <0,抛出异常
      if (millis < 0) {
        TEVENT (wait - throw IAX) ;
        THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
      }
      //获取ObjectMonitor,调用wait()方法
      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);
    }
    

    发现最终是调用ObjectMonitor得wait()方法将线程加入阻塞队列,ObjectMonitor类定义源码位置:openjdk\hotspot\src\share\vm\runtime\objectMonitor.hpp,下面是ObjectMonitor类的部分定义(抽取了重要属性):

      friend class ObjectSynchronizer;
      friend class ObjectWaiter;
      volatile markOop   _header;       // 对象头
      void*     volatile _object;       // backward object pointer - strong root
      void *  volatile _owner;          // 该锁的拥有着
      volatile jlong _previous_owner_tid; //  上一个拥护该锁的线程ID
      ObjectWaiter * volatile _EntryList ;     // 等待锁释放的线程
      volatile intptr_t  _waiters;      // 等待线程的数量
      ObjectWaiter * volatile _WaitSet; // 等待锁的线程列表,调用wait()方法,里面的线程需要等待notify()方法的调用
    

    在ObjectMonitor中定义了_WaitSet属性,是ObjectWaiter类型的一个指针,该类表示等待锁的线程,具体的定义如下:

    class ObjectWaiter : public StackObj {
     public:
      enum TStates { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER, TS_CXQ } ;
      enum Sorted  { PREPEND, APPEND, SORTED } ;
      ObjectWaiter * volatile _next; //前一个ObjectWaiter
      ObjectWaiter * volatile _prev;//后一个ObjectWaiter
      Thread*       _thread; //等待的线程
      jlong         _notifier_tid;
      ParkEvent *   _event;
      volatile int  _notified ;
      volatile TStates TState ;
      Sorted        _Sorted ;           // List placement disposition
      bool          _active ;           // Contention monitoring is enabled
     public:
      ObjectWaiter(Thread* thread);
    
      void wait_reenter_begin(ObjectMonitor *mon);
      void wait_reenter_end(ObjectMonitor *mon);
    };
    

    通过查看ObjectWaiter类的定义,得知ObjectMonitor通过一个双向链表来保存等待该锁的线程。当调用Object.wait()方法时,在JVM中会调用ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS)方法,下面时方法的具体实现:

    void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
      ...................
       // 创建ObjectWaiter,添加到_WaitSet队列中
       ObjectWaiter node(Self);
       node.TState = ObjectWaiter::TS_WAIT ;
       Self->_ParkEvent->reset() ;
       OrderAccess::fence();          // ST into Event; membar ; LD interrupted-flag
    
     //WaitSetLock保护等待队列。通常只锁的拥有着才能访问等待队列
       Thread::SpinAcquire (&_WaitSetLock, "WaitSet - add") ;
     //加入等待队列,等待队列是循环双链表
       AddWaiter (&node) ;
       //使用的是一个简单的自旋锁
       Thread::SpinRelease (&_WaitSetLock) ;
       .....................
    }
    

    调用wait()方法以后线程进入等待队列,在其他线程中调用notify方法或者notifyAll()唤醒等待的队列。在JVM中,通过调用void ObjectMonitor::notify(TRAPS) 方法唤醒等待的线程。由于篇幅问题具体代码,请自行查看JDK源码。

    结束线程

    线程的结束其实就是结束run()方法的调用,具体有三种方案。

    方案一: 通过判断变量的值结束线程,参考代码如下:

    public class ThreadExample extends Thread {
        private volatile boolean flag = true;
        @Override
        public void run() {
            while (flag) {
                System.out.println(".....");
            }
            System.out.println("sub thread end....");
        }
        public static void main(String[] args) throws InterruptedException {
            ThreadExample example = new ThreadExample();
            example.start();
            Thread.sleep(1000 * 3);
            System.out.println("asking thread stop.....");
            example.flag = false;
            Thread.sleep(1000 * 3);
            System.out.println("main thread stop......");
        }
    }
    

    在该程序中,通过判断一个boolean类型的变量来决定是否跳出while循环,结束run()方法的调用。

    方案二: Thread.interrupt()

    调用Thread实例的 interrupt() 方法,接下来该方法会调用

     // Just to set the interrupt flag(该方法只是设置一个中断标志)
    private native void interrupt0();
    

    所以说, interrupt() 方法不会真正的中断一个线程,这是实在一个中断标志,在线程中判断线程标志,结束run()方法的调用。参考代码如下:

    public class ThreadExample2 extends Thread {
        public static void main(String[] args) throws InterruptedException {
            ThreadExample2 test = new ThreadExample2();
            test.start();
            Thread.sleep(1000 * 3);
            test.interrupt();
            Thread.sleep(1000 * 3);
            System.out.println("main thread end .....");
        }
        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println(".......");
            }
            System.out.println("sub thread end.....");
        }
    }
    

    方案三:Thread.interrupt()+变量的值判断

    如果线程已经调用了Thread.sleep(),wait()方法导致线程阻塞,线程不会去判断变量的值中断线程,在阻塞状态调用 interrupt() 会抛出一个中断信号,使得线程退出阻塞,同时清除中断标志。可以结合:Thread.interrupt()+变量的值判断实现线程的停止,参考代码如下:

    class ThreadExample3 extends Thread {
        volatile boolean stop = false;
        public static void main(String args[]) throws Exception {
            ThreadExample3 thread = new ThreadExample3();
            thread.start();
            Thread.sleep(3000);
            System.out.println("Asking thread to stop...");
            /*
             * 如果线程阻塞,将不会检查此变量,调用interrupt之后,线程就可以尽早的终结被阻 塞状 态,能够检查这一变量。
             */
            thread.stop = true;
            /*
             * 这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退 出阻 塞的状态
             */
            thread.interrupt();
            Thread.sleep(3000);
            System.out.println("main thread end...");
            System.exit(0);
        }
        public void run() {
            while (!stop) {
                System.out.println("...........");
                try {
                    Thread.sleep(1000 * 10);
                } catch (InterruptedException e) {
                    // 接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态
                    System.out.println("Thread interrupted...");
                }
            }
            if(Thread.currentThread().isInterrupted()) {
                System.out.println("-------有中断标志---------");
            }else {
                System.out.println("-------无中断标志---------");
            }
            System.out.println("Thread exiting under request...");
        }
    }
    

    Ok,今天的总结完毕,跑路。。。。。。。。。
    参考:http://blog.csdn.net/lirenzuo/article/details/75911548

    相关文章

      网友评论

        本文标题:Java多线程-线程状态

        本文链接:https://www.haomeiwen.com/subject/ckeuextx.html