五态模型:在线程的生命周期中,有五种状态,分别是新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)。
当程序使用new关键字创建一个线程后,该线程就处于新建状态;当调用start()方法后,该线程就处于就绪态。
启用线程使用start()方法,不能使用run()方法!如果直接调用run()方法,系统把线程对象当作普通对象,run()方法当作普通方法而不是线程执行体。
只能对处于就绪态的线程调用start()方法,否则将引发IllegalThreadStateException异常。
如果希望调用子程序的start()方法后子线程立即执行,可以使用Thread.sleep(1)让当前运行的线程睡眠1毫秒。因为这一毫秒CPU不会空闲,它会去执行另一个就绪的线程。
处于就绪态的线程获得CPU进入运行态。但一个线程一般不会一直处于运行态,当发生下面的情况时,线程将进入阻塞态:
· 线程调用sleep()方法主动放弃所占用的处理器资源。
· 线程调用一个阻塞式IO方法,在该方法返回前该线程被阻塞。
· 线程试图获得一个同步监视器,但该监视器正被其他线程所持有。
· 线程在等待某个通知(notify)。
· 程序调用了线程的suspend()方法将线程挂起。但这个方法容易导致死锁,不建议使用。
针对上面的几种情况,当发生一下情况时线程会解除阻塞态重新进入就绪态:
· 调用sleep()方法经过了指定时间。
· 线程调用的阻塞式IO已经返回。
· 线程成功地获取了试图取得的同步监视器。
· 线程正在等待某个通知时,其他线程发出了通知。
· 处于挂起的线程被调用了resume()恢复方法。
注意:线程从阻塞态只能进入就绪态,不能直接进入运行态。调用yield()方法可以让运行态的线程进入就绪态。
线程会以下面三种方式结束,进入死亡状态:
· run()或call()方法执行完成,线程正常结束。
· 线程抛出一个未捕获的Exception或Error。
· 直接调用stop()方法结束线程----该方法容易导致死锁,不建议使用。
注意:
当主线程死亡时,其他线程不受影响,并不会随之结束。一旦子线程启动后,它就和主线程有着相同的地位,不受主线程影响。
可以用isAlive()方法测试一个线程是否死亡。当线程处于就绪、运行、阻塞时返回true,处于新建、死亡时,返回false。
不要对处于死亡状态的线程调用start()方法,对新建状态的线程调用两次start()方法也是错误的。都会引发IllegalThreadStateException异常。
Thread提供了一个让一个线程等待另一个线程完成的方法----join()方法。当某个程序执行流中调用其他线程的join()方法时,调用线程就会阻塞,直到被join线程执行完毕为止。
join()方法有以下三种重载形式:
1. join():等待被join线程执行完成。
2. join(long millis):等待被join线程的时间最长为millis毫秒,超出时间则不再等待。
3. join(long millis, int nanos):等待被join线程最长为millis毫秒加nanos毫微秒。一般不使用该形式,一则程序对时间的精度无需精确到毫微秒;二则计算机硬件、操作系统无法精确到毫微秒。
这种线程在后台运行,为其他线程提供服务。也叫“守护线程”、“精灵线程”。JVM的垃圾回收线程就是典型的后台线程。
后台线程有个特征:如果所有前台线程死亡,后台线程会自动死亡。
调用Thread对象的setDaemon(true)方法可以将线程指定为后台线程,注意,该设置必须在线程启动之前设置,也就是说setDaemon(true)必须在start()方法之前调用,否则会引发IllegalThreadStateException异常。判断一个线程是否为后台线程用Thread类的isDaemon()。
前台线程创建的子线程默认是前台线程,后台线程创建的子线程默认是后台线程。
如果需要让当前正在执行的线程暂停一段时间并进入阻塞状态,可以调用Thread对象的sleep()方法来实现。sleep()方法有两种重载方式。
· static void sleep(long millis): 让当前正在执行的线程暂停millis毫秒并进入阻塞状态。
· static void sleep(long millis,int nanos): 让当前正在执行的线程暂停millis毫秒加nanos毫微秒并进入阻塞状态。同样的原因不建议使用第二种形式。
当一个线程调用sleep()进入阻塞状态后,在其睡眠时间内不会获得执行机会,即使当前系统中没有其他可执行线程。因此sleep()常用来暂停程序的执行。
yield()和sleep()有点类似,它也可以让当前正在执行的线程暂停,但它不会阻塞线程,只是将该线程转入就绪态。yield()只是让线程暂停一下,让系统重新调度一下。完全有可能一个线程调用yield()后又立即被调度出来执行。
sleep()和yield()的区别:
· sleep()方法暂停当前线程后会给其他线程机会,不理会其他线程的优先级;yield()只会给优先级相同或更高的线程机会。
· sleep()方法会将线程转入阻塞态,而yield()方法不会阻塞线程。
· sleep()方法声明抛出了InterruptedException异常,所以调用sleep()方法要么捕捉异常要么显式声明抛出异常;而yield()方法没有声明抛出任何异常。
· sleep()方法比yield()方法有更好的移植性,通常不建议使用yield()方法控制并发线程的执行。
每个线程默认的优先级都和它的父线程相同。一般情况下main()具有一般优先级,由它创建的子线程也具有一般优先级。
Thread类提供了setPriority(int newPriority)、getPriority()方法来设置和获取优先级。setPriority()方法参数是一个1~10的整数,也可以使用Thread类的三个静态常量:
· MAX_PRIORITY: 其值为10;
· MIN_PRIORITY: 其值为1;
· NORM_PRIORITY: 其值为5;
因为有些操作系统不支持10个这么多的优先级,所以为了程序的兼容性最好使用三个静态常量。