Java多线程(二)线程调度
Java多线程(二)
1.线程的生命周期及状态转换
六种状态:
- NEW(新建状态):创建一个线程后,该线程对象就处于新建状态,此时它不能运行,仅仅由JVM为其分配了内存,没有表现出任何线程的动态特征。
- RUNNABLE(可运行状态):新建状态的线程调用start()方法就会进入可运行状态。在RUNNABLE状态又可细分成两种状态:READY(就绪状态)和RUNNING(运行状态),并且线程可以在二者间转换。
- 就绪状态:调用start()方法后等待JVM调度。
- 运行状态:线程对象获得JVM调度,若存在多个CPU,那么允许多个线程并行运行。
- BLOCKED(阻塞状态):处于运行状态的线程可能会因为某些原因失去CPU执行权,暂时停止运行进入阻塞状态。此时JVM不会给线程分配CPU,直到线程重新进入就绪状态,才有机会转换到运行状态。一般以下两种情况会进入阻塞状态:
- 线程A运行中,试图获取同步锁时,却被线程B获取,此时JVM把线程A存到对象的锁池中。
- 线程运行过程中发出I/O请求时,此时该线程也会进入阻塞状态。
- WAITING(等待状态):将运行状态的线程调用了无时间参数限制的方法后,如:wait()、join()等方法,就会将当前运行中的线程转换为等待状态。处于该状态时,必须等其他线程执行特定操作后,才有机会再次争夺CPU使用权,将等待状态的线程转换为运行状态。
- TIMED_WAITING(定时等待状态):将运行状态的线程转换为定时等待状态与转换为等待状态操作类似,只是运行线程调用了有时间参数限制的方法。
- TERMINATED(终止状态):线程的run()方法、call()方法正常执行完毕或线程抛出了一个未捕获的异常、错误,线程就进入终止状态。进入终止状态,线程不在拥有运行资格,也不能转换到其他状态,生命周期结束。
2.线程的调度
线程调度的两种模型:
- 分时调度模型:让所有线程轮流获得CPU使用权。
- 抢占式调度模型:让可运行池中所有就绪状态的线程抢占CPU的使用权,优先级高的线程获得CPU执行权的概率较大。
线程优先级
对线程进行调度,最直接的方法是设置线程优先级,用1~10之间的整数来表示,数字越大优先级越高。也可以使用Thread类的三个静态常量表示线程优先级。
static int MAX_PRIORITY //最高优先级,相当于10
static int MIN_PRIORITY //最低优先级,相当于1
static int NORM_PRIORIY //普通优先级,相当于5
程序运行期间,就绪状态的每个线程都有优先级,如main()具有普通优先级。线程优先级不是固定不变的,可以通过Thread类的setPriority(int newPriority)方法进行设置。
举例:
public class Page364 {
public static void main(String[] args) {
Thread thread1=new Thread(()->{
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"正在输出i:"+i);
}
},"优先级较低的线程");
Thread thread2=new Thread(()->{
for(int j=0;j<10;j++){
System.out.println(Thread.currentThread().getName()+"正在输出j:"+j);
}
},"优先级较高的线程");
thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(10);
thread1.start();
thread2.start();
}
}
3.线程休眠
使用静态方法sleep(long millis)方法使线程暂停一段时间,这样其他线程就可以得到执行机会。但该方法会抛出InterruptedException异常,因此调用该方法时应捕获异常或声明抛出异常。
举例:
public class Page365 {
public static void main(String[] args) {
Thread thread1=new Thread(()->{
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"正在输出i:"+i);
if(i==2){
try{
Thread.sleep(500);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
});
Thread thread2=new Thread(()->{
for(int j=0;j<10;j++){
System.out.println(Thread.currentThread().getName()+"正在输出j:"+j);
}
});
thread1.start();
thread2.start();
}
}
4.线程让步
线程让步可以通过yield()方法来实现,与sleep()有点类似,都可以让正在运行的线程暂停,但yield()不会阻塞该线程,它只是将线程转换成就绪状态,让系统的调度器重新调度一次,,使用yield()后,与当前线程优先级相同或更改的线程可以获得执行机会。
举例:
class YieldThread extends Thread{
public YieldThread(String name){
super(name);
}
public void run(){
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
if(i==2){
System.out.println("线程让步");
Thread.yield();
}
}
}
}
public class Page367 {
public static void main(String[] args) {
Thread thread1=new YieldThread("thread1");
Thread thread2=new YieldThread("thread2");
thread1.start();
thread2.start();
}
}
5.线程插队
Thread类中提供了join()方法,在某个线程中调用其他线程的join()方法时,调用的线程将被堵塞,直到被join()方法加入的线程执行完成之后它才会继续运行。
举例:
class EmergencyThread implements Runnable{
public void run(){
for(int i=1;i<6;i++){
System.out.println(Thread.currentThread().getName()+"输入:"+i);
}
}
}
public class Page368 {
public static void main(String[] args) throws InterruptedException{
Thread thread1=new Thread(new EmergencyThread(),"thread1");
thread1.start();
for(int i=1;i<6;i++){
System.out.println(Thread.currentThread().getName()+"输入:"+i);
if(i==2){
thread1.join();
}
}
}
}