JDK8源码研究(三):Thread
首先来看Thread类的继承关系,比较简单,只实现了Runable接口,该接口定义了线程具体的执行任务的run()方法。
然后定义了一个类的内部的静态代码块,保证registerNatives()方法在类初始化时候能最先执行。
接下来我们看下该类的属性,可以看到一些熟悉的属性,如name记录的是该线程实例的线程名,priority记录的是该线程的优先级,daemon记录该线程是否为守护线程,target属性放置了要运行的Runbale对象。contextClassLoader放置了该线程的上下文类加载器,group放置了该线程对应的线程组,threadInitNumber这个静态属性记录了自动编号匿名线程的编号值。
再来看看Thread类的构造方法,共五个参数,分别是线程组,Runable对象,线程名,栈大小,权限控制上下文,是否继承ThreadLocals。其他的构造方法都是基于该构造方法,其没有的参数会给与一定的默认值。逻辑都比较简单,大部分情况下,没有指定属性则会继承父线程的属性。
Thread是不能使用clone()的方法的,会直接抛出一个CloneNotSupportException异常。
接下来我们看看最常用的start()方法,首先会检测线程状态是否为0,即未启动状态,如果不是则会抛出一个异常,然后在线程组中加入该线程,最后启动线程,如果启动失败,将会将该线程放入线程组的失败列表中。该方法使用了synchronized修饰,进入该方法时候会先获取线程对象的锁。
从run()方法可以看出,当我们既继承了Thread类,又把Runbale实现传入的时候,会使用传入的Runable来执行。
sleep方法接受两个参数,一个毫秒,一个纳秒,从代码中可以看出来,java其实并没有纳秒级别的sleep,当纳秒值大于500000的时候把毫秒加一而已。
两个检查是否中断的方法,interrupted会清除中断标记,而isInterrupted则不会。
join这个方法会使当前线程阻塞(注意当前调用该方法的线程,而不是线程对象这个线程,比如main线程调用testThread线程对象的join方法,则main线程会阻塞,直到testThread线程运行完成),内部逻辑也比较简单,就是调用其wait()方法。线程实例运行完成后应该会唤醒该线程。