线程的生命周期
线程的生命周期会经历以下几个状态:
(1)新建:new创建线程对象时
(2)就绪:调用start()方法时
(3)运行:调用run()方法时
(4)阻塞:多种原因可导致阻塞
(5)超时等待:该状态不同于WAITING,它可以在指定的时间后自行返回。
(6)死亡:多种原因
新建、就绪状态:
(1)使用new关键字创建一个线程时,该线程处于新建状态;
(2)线程对象调用start()方法时,该线程处于就绪状态(线程获得CPU,等待执行)
(3)线程的启动是从调用start()方法开始的,而不是run()方法
(4)永远不要调用线程对象的run()方法
①如果直接调用 run() 方法,系统会把该线程对象当成普通对象,run()方法也将变成一个普通方法(而不是线程执行体)而立即执行;
②如果直接调用了 run() 方法,则该线程不再处于新建状态,不能再次调用 start()方法,否则会报IllegalThreadStateException异常;
③如果直接调用了 run() 方法,则在run() 方法里不能直接通过this.getName() 方法获得线程名(此时获取的是对象名,因为此时已经没有线程体了,线程对象变成了普通对象),而是通过Thread.currentThread().getName() 获得。
(5)如果希望线程对象调用start()方法后立即执行run()方法(线程体),可通过Thread.sleep(1)让主线程睡眠1毫秒,而给其他线程执行机会。
运行、阻塞状态:
(1)如果CPU是单核,则在任一时刻都只有一个线程在执行。当线程数据大于核数时,就会出现线程轮换。
(2)所有桌面和服务器系统都采用的是抢占式调度策略,即当前线程在系统允许的执行时间之后,就给其他线程获得执行机会,且优先给优化级高的线程。
(3)有些小型系统如手机会采用协作式调度策略,即只能当前线程主动放弃所占资源(调用sleep()或yield()方法)
(4)线程从阻塞状态解除后只能转变为就绪状态,而就绪状态变为运行状态只能由系统线程调度转换。
(5)线程调用了yield()方法也会重新进入就绪状态
发生以下情况时,线程将进入阻塞状态:
①调用sleep()方法时。此时会放弃它所占用的处理器资源。【过了sleep指定时间不再阻塞】
②调用一个阻塞式IO方法还没有返回之前,该线程被阻塞。【阻塞IO方法返回后不再阻塞】
③试图获取一个正被其他线程所持有的同步监视器。【拿到监听器不再阻塞】
④等待通知时(notify)。【其他线程调用了notify时不再阻塞】
⑤调用suspend()方法将程序挂起时。【线程调用resume()方法时撤销挂起时不再阻塞】
超时等待
处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。
线程死亡
(1)线程正常结束(run或call方法执行完)
(2)线程跑出一个未捕获的Exception或Error
(3)线程自己调用stop()方法(该方法容易导致死锁)
一旦子线程启动后,它就拥有和主线程相同的地位,不受主线程的影响;线程对象只能调用一次start()方法,且只能在新建状态时才能调用,否则会抛出IllegalThreadStateException异常。