多线程实现之Java

来源:互联网 发布:pc群控手机源码 编辑:程序博客网 时间:2024/04/29 01:55

关于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 {    NEW,    RUNNABLE,    BLOCKED,       WAITING,    TIMED_WAITING,    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{@Overridepublic 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>{@Overridepublic 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结构
从上图可以很清晰的看到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 thenit 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 thisif (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 runningjlong 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的回调。

0 0
原创粉丝点击