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