3、线程状态
线程存在多种状态,包括新状态、可运行状态、运行状态、等待/被阻塞/睡眠/监控/挂起状态和死状态5中状态。
1)、新状态
新状态是指线程实例化后,但start方法还没有在该线程上被调用时所处的状态。它还没有达到准备运行的状态,更不是一个执行线程。
2)、可运行状态
Start()方法启动后,线程由新状态进入可运行状态。可运行状态就是准备好,可以运行但还没有运行的状态。
不但stat()方法可以进入运行状态,而且从等待/被阻塞/睡眠/监控/挂起状态也可以进入可运行状态。如调用sleep()方法结束后,调用yield(0方法结束后所作用的线程都进入可运行状态。
可运行状态也可以说是CPU选择线程的状态。所有的线程不分主次、前后,只要处于可运行状态都有可能被CPU选中运行,当然有可能本线程处于可运行状态,但是很长时间不运行它,这个是一种特殊情况。还有其中情况就是总是运行一个线程,如在一个线程中调用yield()方法后进入可运行状态,但是下面运行的还是这个线程。
3)、运行状态
就是当前执行线程所处的状态,它可以转入可运行状态和等待/被阻塞/失眠/监控/挂起状态,但是只能从可运行状态进入运行状态。
4)、等待/被阻塞/睡眠/监控/挂起状态
等待/被阻塞/睡眠/监控/挂起状态很复杂,它是很多状态的集合体,但是都起到从运行状态到可运行状态的中介作用。处于运行状态的线程由于某种原因不能继续运行,例如调用sleep()方法、被调用join()方法,此线程就会由运行状态进入等待/被阻塞/睡眠/监控/挂起状态。而当sleep()方法结束后或调用join()方法的线程结束后,该线程就又进入可运行状态。
进入等待/被阻塞/睡眠/监控/挂起状态的方法除了sleep()、yield()方法和join()方法外,还有suspend()方法,它是指让一个线程把另一个线程挂起。
5)、死状态
当线程中的run()方法完成之后,线程就进入了死状态。对处于一个死状态的线程带哦用start()方法会发生异常。
4、线程的调度
线程调度程序是JVM中的一部分,它决定在任意指定的时刻应该运行哪个线程,并吧线程带出运行状态。线程的调度定义了java运行环境如何交换任务以及如何选择下一个即将被执行的任务。对线程进行调度又通过优先级、sleep()、yield()、join()方法来完成。在前面说过CPU对线程的选择是不确定的,所以通过调度知识让线程按照某种方式运行,而不能彻底规定它。
1)、优先级
用来判定何时允许某个线程运行。理论上优先级高的线程比优先级低的线程可获得更多的CPU时间。实际上获得CPU时间长度与很多因素有关,不能仅靠优先级来判断。设计线程的优先级使用setPriority(int level)方法来设置。在level值中,用MIN_PRIORITY来表示优先级最小1;用MAX_PRIORITY来表示优先级最大10;线程的默认优先级为5即NORM_PRIORITY。
获得当前线程的优先级的方法是getPriority(),其一般形式为getPriority()。
看下面应用优先级的程序,它通过执行循环、记录次数来表现出优先级的高低:
public class test{ public static void main(String[] str) { Thread.currentThread().setPriority(Thread.MAX_PRIORITY); MyThread t1 = newMyThread(Thread.NORM_PRIORITY + 2); MyThread t2 = newMyThread(Thread.NORM_PRIORITY - 2); t1.start(); t2.start(); try { Thread.sleep(1000); } catch (Exception e) { } t1.stop(); t2.stop(); try { t1.t.join(); t2.t.join(); } catch (Exception e) { } System.out.println("低优先级线程" +t2.click); System.out.println("高优先级线程" +t1.click); } } class MyThread implementsRunnable{ int click = 0; Thread t; private volatile boolean running = true; public MyThread(int p){ t = new Thread(this); t.setPriority(p); } @Override public void run() { while (running) { click++; } } public void stop() { running = false; } public void start() { t.start(); } }
该程序的输出结果与CPU有关,运行本程序还能看出CPU的速度。
2)、sleep睡眠方法
线程sleep()是一个静态方法,它强制线程进入睡眠状态。在线程中使用sleep()方法是非常必要的,它起到使线程停顿的作用。Sleep()方法可能抛出InterruptedException异常,在使用sleep()方法时一定要进行异常处理。
public class test{ public static void main(String[] str) { MyThread1 t1 = new MyThread1(); MyThread2 t2 = new MyThread2(); MyThread3 t3 = new MyThread3(); t1.start(); t2.start(); t3.start(); } } class MyThread1 extendsThread{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.print("●"); try { Thread.sleep(1000); } catch (Exception e) { System.err.println("异常:" + e); } } } } class MyThread2 extendsThread{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.print("■"); try { Thread.sleep(1000); } catch (Exception e) { System.err.println("异常:" + e); } } } } class MyThread3 extendsThread{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.print("▲"); try { Thread.sleep(1000); } catch (Exception e) { System.err.println("异常:" + e); } } } }
我们可以看到运行结果还是不完全确定的,sleep()方法也只是让每个线程都有运行的机会。
3)、join加入方法
Join()方法使一个线程1跟在当前运行的线程2的后面运行,当线程1运行完后再继续运行线程2。我们先来看一个例子:
public class test{ public static void main(String[] str) { MyThread1 t1 = new MyThread1(); MyThread2 t2 = new MyThread2(); MyThread3 t3 = new MyThread3(); t2.mt1 = t1; t1.start(); t2.start(); t3.start(); } } class MyThread1 extendsThread{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println("●"); try { Thread.sleep(5000); } catch (Exception e) { System.err.println("异常:" + e); } } } } class MyThread2 extendsThread{ public MyThread1 mt1; @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println("■"); try { Thread.sleep(5000); } catch (Exception e) { System.err.println("异常:" + e); } if (i == 10) { try { mt1.join(); } catch (Exception e) { System.err.println("异常:" + e); } } } } } class MyThread3 extendsThread{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println("▲"); try { Thread.sleep(5000); } catch (Exception e) { System.err.println("异常:" + e); } } } }
我们可以看到运行结果为:
●▲■●■▲▲●■▲●■▲■●●▲■●■▲●■▲■▲●■●▲▲●■● (i=10) ▲▲●▲●▲●▲●▲●▲●●▲●▲■■■■■■■■■成功构建 (总时间: 29 秒)
当i = 10后,我们看到只打印出三角和实心圆,即线程t1和t3,当t1运行结束后,我们看到又打印出了正放心,即t2。
4)、yield让步方法
yield()方法使当前运行的线程回到可运行状态,状态的问题将在第五小节讲到。Yield()方法的作用是让有相同优先级的线程获得运行机会。yield()方法通常被称为让步方法,但它往往达不到让步的目的,因为它只是让当前线程回到可运行的状态,很可能运行的线程又会是这一个。下面的例子使用默认优先级讲解yield()方法:
public class test{ public static void main(String[] str) { MyThread1 t1 = new MyThread1(); MyThread2 t2 = new MyThread2(); MyThread3 t3 = new MyThread3(); t1.start(); t2.start(); t3.start(); } } class MyThread1 extendsThread{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.print("●"); Thread.yield(); } } } class MyThread2 extendsThread{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.print("■"); Thread.yield(); } } } class MyThread3 extendsThread{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.print("▲"); } } }
我们对t1和t2执行yield()方法,执行结果会先打印出一连串的三角形,即t3。但让步没有完全成功,在三角形中仍然还有正方形和圆形:
■●▲▲▲▲▲▲▲●■●▲▲■▲▲▲▲▲▲▲▲▲▲▲●■■●■●■●●■●●■●●●■●■●■●■●■●■●■●■■■■成功构建 (总时间: 0 秒)