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对象来执行的。
实际上是调用了本地方法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