java线程实现

    xiaoxiao2025-09-20  134

    线程的生命周期及五种基本状态关于Java线程的生命周期,请看下面这张图:新建状态(New):当线程对象创建后,即进入了新建状态,如:Thread t = new MyThread();就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。阻塞状态有以下几种进入条件:1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。4.调用线程的suspend方法主动挂起,不过这个方法目前不推荐使用。出现上述几种条件对应的退出条件,阻塞状态会回到就绪状态,重新等待CPU调度执行。阻塞状态需要再次进入就绪状态之后才能再次进入运行状态。死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

    在java线程实现中,针对阻塞状态有做了一定的细分,分为了  BLOCKED ,WAITING,TIMED_WAITING 几种,分别对应等待 进入同步代码块,等待唤醒 ,有时间的等待唤醒几种。具体枚举定义代码如下: public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }

     

    Java线程实现几种方式1.继承Thread类型,覆写run()方法,run方法里面实现自己需要的逻辑,new Thread()之后 线程进入创建状态,start()之后进入就绪状态,进入运行状态时机取决于系统CPU调度。

    public class MyThread extends Thread{ public void run(){ System.out.println(Thread.currentThread().getName()); } } //线程启动  new MyThread().start(); 

     

    2.实现Runnable接口,实现其run()方法,创建其实例,并通过该实例构造 Thread ,通过Thread来启动。 public class MyRunnable implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } } //线程启动 Runnable runnable =new MyRunnable() ; new Thread(runnable).start(); 查看Thread类的run方法可以发现 其实现如下: public void run() { if (target != null) { target.run(); } }

     

    实际执行了我们传入Runnable对象的run方法。 3.jdk1.5之后又提供了另一种方式,通过实现Callable接口来创建线程,提供有返回值的call方法,来达到获得线程返回值的目的,外部线程可以通过future接口可以获得线程执行结果。 public class MyCallable implements Callable<String>{ @Override public String call() throws Exception { return "my name is MyCallable"; } }

     

    使用callable创建线程的启动方式稍微复杂,代码如下: Callable<String> callable = new MyCallable(); FutureTask<String> futureTask = new FutureTask<String>(callable); new Thread(futureTask).start(); System.out.println(futureTask.get());

     

    从上面可以看到callable和前两种实现方式的差别是Callable可以获得返回值。 除了我们手动创建FutureTask的方式启动以外,还可以通过jdk提供的并发执行器进行执行,不需要我们自己手动创建FutureTask对象来执行,代码如下: Callable<String> callable = new MyCallable(); ExecutorService executorService =Executors.newFixedThreadPool(1); Future<String> future = executorService.submit(callable) ; System.out.println(future.get()); executorService.shutdown();

     

    执行器同样可以用来执行Runnable对象,代码类似,只是不能获得返回值。 下面看看Callable如何做到返回线程执行结果的,首先我们看看FutureTask的类继承关系: 从上图可以很清晰的看到FutureTask实际也实现了Runnable接口 ,其run方法实现如下: public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }

     

    所以可以看出来Callable实际就是在Runnable进行了一层包装达到可以携带执行返回结果的目的。 所以总结下来,其实后两种实现都是在Thread基础上进行封装的,实际的执行都是靠Thread对象来执行的。

    线程的start方法调用之后究竟会发生什么首先我们看一下Thread类的start方法

    public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */ group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } }

     

    实际上是调用了本地方法start0() 本地方法,定义在Thread.c文件里面 static JNINativeMethod methods[] = { {"start0", "()V", (void *)&JVM_StartThread}, {"stop0", "(" OBJ ")V", (void *)&JVM_StopThread}, {"isAlive", "()Z", (void *)&JVM_IsThreadAlive}, {"suspend0", "()V", (void *)&JVM_SuspendThread}, {"resume0", "()V", (void *)&JVM_ResumeThread}, {"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority}, {"yield", "()V", (void *)&JVM_Yield}, {"sleep", "(J)V", (void *)&JVM_Sleep}, {"currentThread", "()" THD, (void *)&JVM_CurrentThread}, {"countStackFrames", "()I", (void *)&JVM_CountStackFrames}, {"interrupt0", "()V", (void *)&JVM_Interrupt}, {"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted}, {"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock}, {"getThreads", "()[" THD, (void *)&JVM_GetAllThreads}, {"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads}, };

     

    从上面的代码可以看出start0实际是调用了JVM_StartThread方法,我们找找JVM_StartThread方法的定义 static void thread_entry(JavaThread* thread, TRAPS) { HandleMark hm(THREAD); Handle obj(THREAD, thread->threadObj()); JavaValue result(T_VOID); JavaCalls::call_virtual(&result, obj, KlassHandle(THREAD, SystemDictionary::Thread_klass()), vmSymbols::run_method_name(), vmSymbols::void_method_signature(), THREAD); } JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) JVMWrapper("JVM_StartThread"); JavaThread *native_thread = NULL; // We cannot hold the Threads_lock when we throw an exception, // due to rank ordering issues. Example: we might need to grab the // Heap_lock while we construct the exception. bool throw_illegal_thread_state = false; // We must release the Threads_lock before we can post a jvmti event // in Thread::start. { // Ensure that the C++ Thread and OSThread structures aren't freed before // we operate. MutexLocker mu(Threads_lock); // Since JDK 5 the java.lang.Thread threadStatus is used to prevent // re-starting an already started thread, so we should usually find // that the JavaThread is null. However for a JNI attached thread // there is a small window between the Thread object being created // (with its JavaThread set) and the update to its threadStatus, so we // have to check for this if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) { throw_illegal_thread_state = true; } else { // We could also check the stillborn flag to see if this thread was already stopped, but // for historical reasons we let the thread detect that itself when it starts running jlong size = java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread)); // Allocate the C++ Thread structure and create the native thread. The // stack size retrieved from java is signed, but the constructor takes // size_t (an unsigned type), so avoid passing negative values which would // result in really large stacks. size_t sz = size > 0 ? (size_t) size : 0; <span style="color:#ff6666;">native_thread = new JavaThread(&thread_entry, sz);</span> // At this point it may be possible that no osthread was created for the // JavaThread due to lack of memory. Check for this situation and throw // an exception if necessary. Eventually we may want to change this so // that we only grab the lock if the thread was created successfully - // then we can also do this check and throw the exception in the // JavaThread constructor. if (native_thread->osthread() != NULL) { // Note: the current thread is not being used within "prepare". native_thread->prepare(jthread); } } } if (throw_illegal_thread_state) { THROW(vmSymbols::java_lang_IllegalThreadStateException()); } assert(native_thread != NULL, "Starting null thread?"); if (native_thread->osthread() == NULL) { // No one should hold a reference to the 'native_thread'. delete native_thread; if (JvmtiExport::should_post_resource_exhausted()) { JvmtiExport::post_resource_exhausted( JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS, "unable to create new native thread"); } THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), "unable to create new native thread"); } Thread::start(native_thread); JVM_END

     

    可以看到在JVM_StartThread方法里面创建了实际跟平台有关系的本地线程,线程函数是thread_entry ,thread_entry定义了实际调用vmSymbols::run_method_name()这个方法,这个方法实际就是定义运行run方法,在vmSymbols.hpp可以看到定义如下: 本地线程最终执行run_method_name这个方法 template(run_method_name,                           "run")   

     

    可以看到线程最终执行我们的Thread对象的run方法。 所以整个调用时序是 Thread.start -->JVM_StartThread -->thread_entry  --> os本地线程(异步) --> Thread.run

    总结

    从上面我们知道java 线程是建立在系统本地线程之上的,通过Thread对象做了一层封装,封装了各个不同平台的细节,提供了统一的API ,Thread之上又做了 Runnable的回调和Callable的回调。

    Java线程创建和执行的三种方式最终都是通过Thread来实现的。

    注:本文源码部分来源于JDK1.7和openJDK7源码,可以从下面地址下载。

    http://download.java.net/openjdk/jdk7

    最新回复(0)