Thread 源码解析
线程是在进程中执行的单位,线程的资源开销相对于进程的开销是相对较少的,所以我们一般创建线程执行,而不是进程执行。 下面我们就来了解一下Java Thread类源码,了解一下线程的控制吧!
构造方法
|
|
上述的的Thread 的构造方法不止是上面的一些内容,还有其他带参数的组合不同的构造方法, 我们可以发现在构造方法里面执行的是init()方法,那么这个init()方法进行的是什么样的操作呢?
通过init()方法,我们可以看到, init是一个私有的方法,其中第一个进行的操作是,判断是否具有线程名,线程名是不可以为空的,如果我们使用的是无参数构造函数,或者使用的是没有传入线程名的构造方法,那么Thread类中是会默认的为我们规定线程名字,即调用的是Thread- nextThreadNum()
,对线程进行编号。 这个方法是同步的,保证了在执行的时候不会进行异步操作,避免了产生有相同的线ID的出现,源码如下:
我们要注意的一点是,Thread的是实现了Runnable接口的,那么我们其中的run
方法应该怎么执行执行呢? 在init()方法中,我们可以找到实现了’run’方法, 它是如下实现的:
原来里面就做了这个简单的工作,调用了target中个run方法呀! 但是这个target是什么东西呢? 就是我们实现了Runnable接口的对象呀,所以我们在构造方法中会传入一个Runnable接口实现对象, 这个就是 target, 所以最红线程执行的run方法是我们传入的 对象中的run方法,看到这个思路,我马上又想到了Spring中集成hibernate中的做法是一样的(你想做的操作,在外面你自己定义好,Spring暴露一个接口给你,你实现了就行,按照他的规范,就传入到Spring内部中去了,人家就帮你执行了)。
接着看init()方法中做了什么: 执行currentThread
方法,而这个方法又是一个本地方法(本地方法是由其他语言编写的,编译成和处理器相关的机器代码,本地方法保存在动态链接库中,即.dll(windows系统)文件中,格式是各个平台专有的,Java方法是与平台无关的,但是本地方法不是,运行中的Java方法调用本地方法时,虚拟机装载包含这个本地方法的动态库, 并调用这个方法,通过本地方法Java程序可以直接访问底层操作系统的资源),接着又做了左了是否有ThreadGroup
的判断,如果没有传入线程组的话, 第一是使用SecurityManager中的ThreadGroup, 如果从SecurityManager 中获取不到ThreadGroup(), 那么就从当前线程中获取线程组(parent.getThreadGroup()),最后做了检验和些参数的赋值。
重要的属性
接下来,我们来看看Thread类中的重要的属性有哪些吧!
线程的优先级在不同的平台上,对应的系统优先级会不同,可能多个优先级对应同一个系统优先级。
优先级高的线程并不一定优先执行,这个由JVM来解释并向系统提供参考.
重要的方法
start( )方法
|
|
start( )方法是同步的,并且是启动这个线程进行执行,Java虚拟机将会调用这个线程的run方法,这样产生的结果是,两个线程执行着,其中一个是调用start()方法的线程执行,另一个线程是执行run方法的线程。在start()方法中,首先进行的石向成状态的判断,如果是一个JVM新启动的线程,那么threadStatus
的状态是为0的,如果线程不为0 将报出异常, 然后将线程添加到group
中, group.add(this)
方法中执行的结果是,通知group, 这个线程要执行了,所以可以添加进group中,并且这个线程组中没有没有启动的数量将减少。然后设置标识为, 接着调用本地方法started0
,如果没有启动成功,将会通知这个线程组,没有启动成功,捕捉的异常将会忽略。
exit( )方法
|
|
这个方法的解释是说,exit( )是由系统调用的,用于线程在真正的退出前进行一些清理的操作。看看里面进行的操作是什么吧,可以看出,里面执行的是group
赋值为null了, 将target引用进行释放,同时释放了threadLocals
所占用的资源,等等属性都赋值为null。
sleep()方法
|
|
sleep()方法在使用线程的时候,用的是比较多的。 这个方法的作用使得当前线程休眠一定的时间,但是这个期间是不释放持有的锁的。这个方法里面首先进行的是休眠时间的判断,然后又是调用本地方法。
stop( )方法
|
|
stop( )方法是停止线程的执行,这个方法是一个不推荐使用的方法,已经被废弃了,因为使用该方法会出现异常情况。
join()方法
join方法是等待该线程执行,直到超时或者终止,可以作为线程通信的一种方式,A线程调用B线程的join(阻塞),等待B完成后再往下执行。 join()方法中重载了多个方法,但是主要的方法是下面的方法。
|
|
interrupt()方法
|
|
interrupt()方法是中断当前的线程, 其实Thread类中有三个方法,比较容易混淆,在这里解释一下。
- interrupt:将相中置为中断状态
- isInterrupt:线程是否中断
- interrupted:返回线程的上次的中断状态,并清除中断状态。
一般来说,阻塞函数:如Thread.sleep、Thread.join、Object.wait等在检查到线程的中断状态的时候,会抛出InteruptedExeption, 同时会清除线程的中断状态。
对于InterruptedException的处理,可以有两种情况:
- 外层代码可以处理这个异常,直接抛出这个异常即可
- 如果不能抛出这个异常,比如在run()方法内,因为在得到这个异常的同时,线程的中断状态已经被清除了,需要保留线程的中断状态,则需要调用Thread.currentThread().interrupt()
线程的状态
|
|
getContextClassLoader()方法
这个方法是上下文类加载器: