Thread类源码剖析
Posted 只会一点java
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Thread类源码剖析相关的知识,希望对你有一定的参考价值。
一、引子
说来也有些汗颜,搞了几年java,忽然发现竟然没拜读过java.lang.Thread类源码,这次特地拿出来晒一晒。本文将剖析Thread类源码(本文后面源码全部默认JDK8),并讲解一些重要的拓展点。希望对大家能有一些帮助。
本文讲解主干全部出自源码和注释,保证了权威性。(注意:网上,某些书中很多观点都是错的,过时的,片面的,所以大家一定要看源码,重要事情说N遍,看源码!看源码!看源码......)
二、JVM线程状态
在正式学习Thread类中的具体方法之前,我们先来了解一下线程有哪些状态,这个将会有助于后面对Thread类中的方法的理解。
自JDK5开始,线程包括以下6个状态,摘自Thread.State:
1 /** 2 * A thread state. A thread can be in one of the following states: 3 * <ul> 4 * <li>{@link #NEW}<br> 5 * A thread that has not yet started is in this state. 6 * </li> 7 * <li>{@link #RUNNABLE}<br> 8 * A thread executing in the Java virtual machine is in this state. 9 * </li> 10 * <li>{@link #BLOCKED}<br> 11 * A thread that is blocked waiting for a monitor lock 12 * is in this state. 13 * </li> 14 * <li>{@link #WAITING}<br> 15 * A thread that is waiting indefinitely for another thread to 16 * perform a particular action is in this state. 17 * </li> 18 * <li>{@link #TIMED_WAITING}<br> 19 * A thread that is waiting for another thread to perform an action 20 * for up to a specified waiting time is in this state. 21 * </li> 22 * <li>{@link #TERMINATED}<br> 23 * A thread that has exited is in this state. 24 * </li> 25 * </ul> 26 * 27 * <p> 28 * A thread can be in only one state at a given point in time.----》JVM中的线程必须只能是以上6种状态的一种。这些状态是JVM状态并不能和操作系统线程状态互相映射。 29 * These states are virtual machine states which do not reflect 30 * any operating system thread states. 31 * 32 * @since 1.5 33 * @see #getState 34 */ 35 public enum State { 36 /** 37 * Thread state for a thread which has not yet started. 38 */ 39 NEW,--->线程刚创建,还未执行(start方法) 40 41 /** 42 * Thread state for a runnable thread. A thread in the runnable 43 * state is executing in the Java virtual machine but it may 44 * be waiting for other resources from the operating system 45 * such as processor. 46 */ 47 RUNNABLE,--->已就绪可运行的状态。处于此状态的线程是正在JVM中运行的,但可能在等待操作系统级别的资源,例如CPU时间片 48 49 /** 50 * Thread state for a thread blocked waiting for a monitor lock. 51 * A thread in the blocked state is waiting for a monitor lock 52 * to enter a synchronized block/method or 53 * reenter a synchronized block/method after calling 54 * {@link Object#wait() Object.wait}. 55 */ 56 BLOCKED,--->阻塞等待监视器锁。处于此状态的线程正在阻塞等待监视器锁,以进入一个同步块/方法,或者在执行完wait()方法后重入同步块/方法。 57 58 /** 59 * Thread state for a waiting thread. 60 * A thread is in the waiting state due to calling one of the 61 * following methods: 62 * <ul> 63 * <li>{@link Object#wait() Object.wait} with no timeout</li> 64 * <li>{@link #join() Thread.join} with no timeout</li> 65 * <li>{@link LockSupport#park() LockSupport.park}</li> 66 * </ul> 67 * 68 * <p>A thread in the waiting state is waiting for another thread to 69 * perform a particular action. 70 * 71 * For example, a thread that has called <tt>Object.wait()</tt> 72 * on an object is waiting for another thread to call 73 * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on 74 * that object. A thread that has called <tt>Thread.join()</tt> 75 * is waiting for a specified thread to terminate. 76 */ 77 WAITING,--->等待。执行完Object.wait无超时参数操作,或者 Thread.join无超时参数操作(进入等待指定的线程执行结束),或者 LockSupport.park操作后,线程进入等待状态。
一般在等待状态的线程在等待其它线程执行特殊操作,例如:等待另其它线程操作Object.notify()唤醒或者Object.notifyAll()唤醒所有。 78 79 /** 80 * Thread state for a waiting thread with a specified waiting time. 81 * A thread is in the timed waiting state due to calling one of 82 * the following methods with a specified positive waiting time: 83 * <ul> 84 * <li>{@link #sleep Thread.sleep}</li> 85 * <li>{@link Object#wait(long) Object.wait} with timeout</li> 86 * <li>{@link #join(long) Thread.join} with timeout</li> 87 * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> 88 * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> 89 * </ul> 90 */ 91 TIMED_WAITING,--->限时等待。Thread.sleep、Object.wait带超时时间、Thread.join带超时时间、LockSupport.parkNanos、LockSupport.parkUntil这些操作会时线程进入限时等待。 92 93 /** 94 * Thread state for a terminated thread. 95 * The thread has completed execution. 96 */ 97 TERMINATED;--->终止,线程执行完毕。 98 }
看了源码6种状态,很多人会迷惑怎么没有Running状态呢?好吧,请相信源码,不要混淆操作系统线程状态和java线程状态。JVM中的线程必须只能是以上6种状态的一种!(见上图枚举State 注释中的红色部分)。
Running其实是早期操作系统下“单线程进程”的状态,如下图:
注意:上图已年久失修,不可参考!!!!
好吧,现在是不是觉得三观被颠覆...
最新JAVA(JVM)线程状态转换如下图:
如上图,可见:RUNNABLE = 正在JVM中运行的(Running)+ 可能在等待操作系统级别的资源(Ready),例如CPU时间片
线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源),只有线程运行需要的所有条件满足了,才进入就绪状态。
当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。
线程在运行状态过程中,可能有多个原因导致当前线程不继续运行下去,比如用户主动让线程睡眠(睡眠一定的时间之后再重新执行)、用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting(睡眠或等待一定的事件)、waiting(等待被唤醒)、blocked(阻塞)。
当由于突然中断或者子任务执行完毕,线程就会被消亡。
三.Thread类中的方法
老规矩,先看源码注释:
/** * A <i>thread</i> is a thread of execution in a program. The Java ---》一个“线程”是在在程序中执行的线程。Java虚拟机允许应用多个线程并发运行。 * Virtual Machine allows an application to have multiple threads of * execution running concurrently. * <p> * Every thread has a priority. Threads with higher priority are--》每个线程都有优先级,优先级高的先执行。线程可能是守护线程或者不是。 * executed in preference to threads with lower priority. Each thread * may or may not also be marked as a daemon. When code running in * some thread creates a new <code>Thread</code> object, the new---》线程的优先级等于创建线程的优先级,当且仅当一个线程是守护线程,创建出来的线程才是守护线程 * thread has its priority initially set equal to the priority of the * creating thread, and is a daemon thread if and only if the * creating thread is a daemon. * <p> * When a Java Virtual Machine starts up, there is usually a single--》通常JVM启动,有一个非守护线程作为主线程。只有当Runtime.exit被调用或者所有非守护线程死亡时(run执行完毕并返回/抛出异常)JVM会停止运行这些线程。 * non-daemon thread (which typically calls the method named * <code>main</code> of some designated class). The Java Virtual * Machine continues to execute threads until either of the following * occurs: * <ul> * <li>The <code>exit</code> method of class <code>Runtime</code> has been * called and the security manager has permitted the exit operation * to take place. * <li>All threads that are not daemon threads have died, either by * returning from the call to the <code>run</code> method or by * throwing an exception that propagates beyond the <code>run</code> * method. * </ul> * <p> * There are two ways to create a new thread of execution. One is to--》两种创建线程的方法:继承Thread类/实现Runnable接口 * declare a class to be a subclass of <code>Thread</code>. This * subclass should override the <code>run</code> method of class * <code>Thread</code>. An instance of the subclass can then be * allocated and started. For example, a thread that computes primes * larger than a stated value could be written as follows: * <hr><blockquote><pre> * class PrimeThread extends Thread { * long minPrime; * PrimeThread(long minPrime) { * this.minPrime = minPrime; * } * * public void run() { * // compute primes larger than minPrime * . . . * } * } * </pre></blockquote><hr> * <p> * The following code would then create a thread and start it running: * <blockquote><pre> * PrimeThread p = new PrimeThread(143); * p.start(); * </pre></blockquote> * <p> * The other way to create a thread is to declare a class that * implements the <code>Runnable</code> interface. That class then * implements the <code>run</code> method. An instance of the class can * then be allocated, passed as an argument when creating * <code>Thread</code>, and started. The same example in this other * style looks like the following: * <hr><blockquote><pre> * class PrimeRun implements Runnable { * long minPrime; * PrimeRun(long minPrime) { * this.minPrime = minPrime; * } * * public void run() { * // compute primes larger than minPrime * . . . * } * } * </pre></blockquote><hr> * <p> * The following code would then create a thread and start it running: * <blockquote><pre> * PrimeRun p = new PrimeRun(143); * new Thread(p).start(); * </pre></blockquote> * <p> * Every thread has a name for identification purposes. More than--》每个线程有自己的名称用来标识自己。但可能多个线程会重名,如果启动时没有创建名字,会自动生成一个。 * one thread may have the same name. If a name is not specified when * a thread is created, a new name is generated for it. * <p> * Unless otherwise noted, passing a {@code null} argument to a constructor * or method in this class will cause a {@link NullPointerException} to be * thrown. * * @author unascribed --》意思是:该代码第一原作者不是我,但我实在也不知道是谁,就记作无名氏吧(版权意识) * @see Runnable * @see Runtime#exit(int) * @see #run() * @see #stop() * @since JDK1.0 */
Thread类实现了Runnable接口,在Thread类中,
关键属性:
name是表示Thread的名字,可以通过Thread类的构造器中的参数来指定线程名字,
priority表示线程的优先级(最大值为10,最小值为1,默认值为5),
daemon表示线程是否是守护线程,如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。在JVM中,垃圾收集器线程就是守护线程。
target表示要执行的任务。
group线程群组
关键方法:
以下是关系到线程运行状态的几个方法:
1)start
start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。
2)run
run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。
3)sleep
sleep方法有两个重载版本:
1 public static native void sleep(long millis) throws InterruptedException; 2 3 public static void sleep(long millis, int nanos) throws InterruptedException;
sleep让线程睡眠,交出CPU,让CPU去执行其他的任务。sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。sleep方法相当于让线程进入阻塞状态。
4)yield
调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。
注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。
5)join
join方法有三个重载版本:
1 join() 2 join(long millis) //参数为毫秒 3 join(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒
可以看出,当调用thread.join()方法后,main线程会进入等待,然后等待thread执行完之后再继续执行。
实际上调用join方法是调用了Object的wait方法,这个可以通过查看源码得知:
wait方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。
6)interrupt
interrupt,中断。单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程;
7)stop
stop方法已经是一个废弃的方法,它是一个不安全的方法。因为调用stop方法会直接终止run方法的调用,并且会抛出一个ThreadDeath错误,如果线程持有某个对象锁的话,会完全释放锁,导致对象状态不一致。所以stop方法基本是不会被用到的。
8)destroy
destroy方法也是废弃的方法。基本不会被使用到。
四、拓展点
1.LookSupport.park()和unpark()原理
LockSupport类是Java6(JSR166-JUC)引入的一个类,提供了基本的线程同步原语。LockSupport实际上是调用了Unsafe类里的函数,归结到Unsafe里,只有两个函数:
挂起
public native void park(boolean isAbsolute, long time);
唤醒
public native void unpark(Thread jthread);
unpark函数为线程提供“许可(permit)”,park函数则等待“许可”。这个有点像信号量,但是这个“许可”是不能叠加的,“许可”是一次性的。
比如线程B连续调用了三次unpark函数,当线程A调用park函数就使用掉这个“许可”,如果线程A再次调用park,则进入等待状态。
注意,unpark函数可以先于park调用。比如线程B调用unpark函数,给线程A发了一个“许可”,那么当线程A调用park时,它发现已经有“许可”了,那么它会马上再继续运行。
实际上,park函数即使没有“许可”,有时也会无理由地返回,这点等下再解析。
park/unpark模型真正解耦了线程之间的同步,线程之间不再需要一个Object或者其它变量来存储状态,不再需要关心对方的状态。
我们从JDK源码开始看,java.util.concurrent.locks.LookSupport.park()如下:
1 /** 2 * Disables the current thread for thread scheduling purposes unless the 3 * permit is available.--->停止当前线程的调度执行一直到许可可达。 4 * 5 * <p>If the permit is available then it is consumed and the call 6 * returns immediately; otherwise the current thread becomes disabled 7 * for thread scheduling purposes and lies dormant until one of three 8 * things happens: 9 *--->当许可条件满足时,当前线程会立即返回。否则会一直停止线程调度并且假死一直到下面3件事情发生: 10 * <ul> 11 * 12 * <li>Some other thread invokes {@link #unpark unpark} with the 13 * current thread as the target; or 14 *--->1.其它线程调用unpark方法唤醒此线程 15 * <li>Some other thread {@linkplain Thread#interrupt interrupts} 16 * the current thread; or 17 *--->2.其它线程中断此线程 18 * <li>The call spuriously (that is, for no reason) returns. 19 * </ul> 20 **--->3.此线程未知错误返回了 21 * <p>This method does <em>not</em> report which of these caused the 22 * method to return. Callers should re-check the conditions which caused 23 * the thread to park in the first place. Callers may also determine, 24 * for example, the interrupt status of the thread upon return.
*----》该方法不会告知是哪个原因导致的返回。调用方需要重新校验导致线程park的条件。比如中断状态。 25 */ 26 public static void park() { 27 UNSAFE.park(false, 0L);//线程调用该方法,线程将一直阻塞直到超时(这里没有超时时间为0),或者是中断条件出现。 28 }
这里我们就简单看一下park()源码,目录:
openjdk-8-src-b132-03_mar_2014\\openjdk\\hotspot\\src\\share\\vm\\runtime\\park.cpp
openjdk-8-src-b132-03_mar_2014\\openjdk\\hotspot\\src\\share\\vm\\runtime\\park.hpp
openjdk-8-src-b132-03_mar_2014\\openjdk\\hotspot\\src\\os\\linux\\vm\\os_linux.cpp
openjdk-8-src-b132-03_mar_2014\\openjdk\\hotspot\\src\\os\\linux\\vm\\os_linux.hpp
park.hpp:
1 class Parker : public os::PlatformParker { 2 private: 3 volatile int _counter ; 4 Parker * FreeNext ; 5 JavaThread * AssociatedWith ; // Current association 6 7 public: 8 Parker() : PlatformParker() { 9 _counter = 0 ; 10 FreeNext = NULL ; 11 AssociatedWith = NULL ; 12 } 13 protected: 14 ~Parker() { ShouldNotReachHere(); } 15 public: 16 // For simplicity of interface with Java, all forms of park (indefinite, 17 // relative, and absolute) are multiplexed into one call. 18 void park(bool isAbsolute, jlong time); 19 void unpark(); 20 21 // Lifecycle operators 22 static Parker * Allocate (JavaThread * t) ; 23 static void Release (Parker * e) ; 24 private: 25 static Parker * volatile FreeList ; 26 static volatile int ListLock ; 27 28 };
os_linux.hpp中,PlatformParker:
1 class PlatformParker : public CHeapObj<mtInternal> { 2 protected: 3 enum { 4 REL_INDEX = 0, 5 ABS_INDEX = 1 6 }; 7 int _cur_index; // which cond is in use: -1, 0, 1 8 pthread_mutex_t _mutex [1] ; 9 pthread_cond_t _cond [2] ; // one for relative times and one for abs. 10 11 public: // TODO-FIXME: make dtor private 12 ~PlatformParker() { guarantee (0, "invariant") ; } 13 14 public: 15 PlatformParker() { 16 int status; 17 status = pthread_cond_init (&_cond[REL_INDEX], os::Linux::condAttr()); 18 assert_status(status == 0, status, "cond_init rel"); 19 status = pthread_cond_init (&_cond[ABS_INDEX], NULL); 20 assert_status(status == 0, status, "cond_init abs"); 21 status = pthread_mutex_init (_mutex, NULL); 22 assert_status(status == 0, status, "mutex_init"); 23 _cur_index = -1; // mark as unused 初始化时-1未使用 24 } 25 };
可以看到Parker类实际上用Posix的mutex,condition来实现的。
在Parker类里的_counter字段,就是用来记录所谓的“许可”的。
park()源码实现,为了保证源码的完整性,就直接在源码上注释原理了。
1 void Parker::park(bool isAbsolute, jlong time) { 2 // Ideally we\'d do something useful while spinning, such 3 // as calling unpackTime(). 4 5 // Optional fast-path check: 6 // Return immediately if a permit is available. 7 // We depend on Atomic::xchg() having full barrier semantics 8 // since we are doing a lock-free update to _counter. 9 if (Atomic::xchg(0, &_counter) > 0) return;//先尝试能否直接拿到“许可”,即_counter>0时,如果成功,则把_counter设置为0,并返回: 10 11 Thread* thread = Thread::current(); 12 assert(thread->is_Java_thread(), "Must be JavaThread"); 13 JavaThread *jt = (JavaThread *)thread; 14 15 // Optional optimization -- avoid state transitions if there\'s an interrupt pending. 16 // Check interrupt before trying to wait 17 if (Thread::is_interrupted(thread, false)) { 18 return; 19 } 20 21 // Next, demultiplex/decode time arguments 22 timespec absTime; 23 if (time < 0 || (isAbsolute && time == 0) ) { // don\'t wait at all 24 return; 25 } 26 if (time > 0) { 27 unpackTime(&absTime, isAbsolute, time); 28 } 29 30 31 // Enter safepoint region 32 // Beware of deadlocks such as 6317397. 33 // The per-thread Parker:: mutex is a classic leaf-lock. 34 // In particular a thread must never block on the Threads_lock while 35 // holding the Parker:: mutex. If safepoints are pending both the 36 // the ThreadBlockInVM() CTOR and DTOR may grab Threads_lock. 37 ThreadBlockInVM tbivm(jt);//如果不成功,则构造一个ThreadBlockInVM, 38 39 // Don\'t wait if cannot get lock since interference arises from 40 // unblocking. Also. check interrupt before trying wait 41 if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) { 42 return; 43 } 44 45 int status ; 46 if (_counter > 0) { // no wait needed然后检查_counter是不是>0,如果是,则把_counter设置为0,unlock mutex并返回: 47 _counter = 0; 48 status = pthread_mutex_unlock(_mutex); 49 assert (status == 0, "invariant") ; 50 // Paranoia to ensure our locked and lock-free paths interact 51 // correctly with each other and Java-level accesses. 52 OrderAccess::fence(); 53 return; 54 } 55 56 #ifdef ASSERT 57 // Don\'t catch signals while blocked; let the running threads have the signals. 58 // (This allows a debugger to break into the running thread.) 59 sigset_t oldsigs; 60 sigset_t* allowdebug_blocked = os::Linux::allowdebug_blocked_signals(); 61 pthread_sigmask(SIG_BLOCK, allowdebug_blocked, &oldsigs); 62 #endif 63 64 OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */); 65 jt->set_suspend_equivalent(); 66 // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self() 67 68 assert(_cur_index == -1, "invariant"); 69 if (time == 0) { 70 _cur_index = REL_INDEX; // arbitrary choice when not timed 71 status = pthread_cond_wait (&_cond[_cur_index], _mutex) ; 72 } else { 73 _cur_index = isAbsolute ? ABS_INDEX : REL_INDEX; 74 status = os::Linux::safe_cond_timedwait (&_cond[_cur_index], _mutex, &absTime) ; 75 if (status != 0 && WorkAroundNPTLTimedWaitHang) { 76 pthread_cond_destroy (&_cond[_cur_index]) ; 77 pthread_cond_init (&_cond[_cur_index], isAbsolute ? NULL : os::Linux::condAttr()); 78 } 79 } 80 _cur_index = -1; 81 assert_status(status == 0 || status == EINTR || 82 status == ETIME || status == ETIMEDOUT, 83 status, "cond_timedwait"); 84 85 #ifdef ASSERT 86 pthread_sigmask(SIG_SETMASK, &oldsigs, NULL); 87 #endif 88 89 _counter = 0 ; 90 status = pthread_mutex_unlock(_mutex) ; 91 assert_status(status == 0, status, "invariant") ; 92 // Paranoia to ensure our locked and lock-free paths interact 93 // correctly with each other and Java-level accesses. 94 OrderAccess::fence(); 95 96 // If externally suspended while waiting, re-suspend 97 if (jt->handle_special_suspend_equivalent_condition()) { 98 jt->java_suspend_self(); 99 } 100 }
unpark()源码实现
1 void Parker::unpark() { 2 int s, status ; 3 status =Android 逆向类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )(代码片段