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 秒)