线程状态:
与人有生老病死一样,线程也同样要经历新建、就绪、运行(活动)、阻塞和死亡五种不同的状态。这五种状态都可以通过Thread类中的方法进行控制。
创建并运行线程:
① 新建状态(New Thread):在Java语言中使用new 操作符创建一个线程后,该线程仅仅是一个空对象,它具备类线程的一些特征,但此时系统没有为其分配资源,这时的线程处于创建状态。
线程处于创建状态时,可通过Thread类的方法来设置各种属性,如线程的优先级(setPriority)、线程名(setName)和线程的类型(setDaemon)等。
② 就绪状态(Runnable):使用start()方法启动一个线程后,系统为该线程分配了除CPU外的所需资源,使该线程处于就绪状态。此外,如果某个线程执行了yield()方法,那么该线程会被暂时剥夺CPU资源,重新进入就绪状态。
③ 运行状态(Running):Java运行系统通过调度选中一个处于就绪状态的线程,使其占有CPU并转为运行状态。此时,系统真正执行线程的run()方法。
a) 可以通过Thread类的isAlive方法来判断线程是否处于就绪/运行状态:当线程处于就绪/运行状态时,isAlive返回true,当isAlive返回false时,可能线程处于阻塞状态,也可能处于停止状态。
④ 阻塞和唤醒线程
阻塞状态(Blocked):一个正在运行的线程因某些原因不能继续运行时,就进入阻塞 状态。这些原因包括:
a) 当执行了某个线程对象的sleep()等阻塞类型的方法时,该线程对象会被置入一个阻塞集内,等待超时而自动苏醒。
b) 当多个线程试图进入某个同步区域时,没能进入该同步区域的线程会被置入锁定集,直到获得该同步区域的锁,进入就绪状态。
c) 当线程执行了某个对象的wait()方法时,线程会被置入该对象的等待集中,知道执行了该对象的notify()方法wait()/notify()方法的执行要求线程首先获得该对象的锁。
⑤ 死亡状态(Dead):线程在run()方法执行结束后进入死亡状态。此外,如果线程执行了interrupt()或stop()方法,那么它也会以异常退出的方式进入死亡状态。
终止线程的三种方法
① 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止,推荐使用。
② 使用stop方法强制终止线程(这个方法不推荐使用,因为stop和suppend、resume一样,也可能发生不可预料的结果)。
③ 使用interrupt方法中断线程。
启动线程的两种方法
你简单的调用run方法是没有这样效果的,所以你必须调用Thread类的start方法来启动你的线程.所以你启动线程有两种方法
一是写一个类继承自Thread类,然后重写里面的run方法,用start方法启动线程
二是写一个类实现Runnable接口,实现里面的run方法,用new Thread(Runnable target).start()方法来启动
这两种方法都必须实现RUN方法,这样线程起动的时候,线程管理器好去调用你的RUN方法.
查看源码可以发现Thread也是实现的Runable
public class Thread implements Runnable {
1) start:
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的 start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
2) run:
run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。
newly created:当我们new Thread()时,线程所处的状态就是新创建的状态(newly created),但是此时系统并不分配资源;给线程发送消息start()时,才分配资源。
runable:给线程发送start()消失时,进入runable状态。此时抢到cpu的线程开始执行run()方法。其他调用start()的线程在queue里排队等待cpu调度。
blocked:线程被挂起时,离开runable状态,知道结束回到runable状态。
a.线程内部调用sleep()时,休息,被挂起。时间到,则运作。
b.给线程发送消息wait()时,等待,被挂起。线程收到消息,notify();
c.某个线程调用join()时,其他线程会等到该线程结束后才会开始执行。会抛出InterruptedException异常,因此要写在try-catch块里。
dead:线程的run()方法执行结束or线程调用了stop()函数时,线程进入销毁状态。
线程状态说明
线程状态从大的方面来说,可归结为:初始状态、可运行状态、不可运行状态和消亡状态,具体可细分为上图所示7个状态,说明如下:
1) 线程的实现有两种方式,一是继承Thread类,二是实现Runnable接口,但不管怎样,当我们new了thread实例后,线程就进入了初始状态;
2) 当该对象调用了start()方法,就进入可运行状态;
3) 进入可运行状态后,当该对象被操作系统选中,获得CPU时间片就会进入运行状态;
4) 进入运行状态后case就比较多,大致有如下情形:
﹒run()方法或main()方法结束后,线程就进入终止状态;
﹒当线程调用了自身的sleep()方法或其他线程的join()方法,就会进入阻塞状态(该状态既停止当前线程,但并不释放所占有的资源)。当 sleep()结束或join()结束后,该线程进入可运行状态,继续等待OS分配时间片;
﹒当线程刚进入可运行状态(注意,还没运行),发现将要调用的资源被锁牢(synchroniza,lock),将会立即进入锁池状态,等待获取锁标记 (这时的锁池里也许已经有了其他线程在等待获取锁标记,这时它们处于队列状态,既先到先得),一旦线程获得锁标记后,就转入可运行状态,等待OS分配 CPU时间片;
﹒当线程调用wait()方法后会进入等待队列(进入这个状态会释放所占有的所有资源,与阻塞状态不同),进入这个状态后,是不能自动唤醒的,必须依靠其 他线程调用notify()或notifyAll()方法才能被唤醒(由于notify()只是唤醒一个线程,但我们由不能确定具体唤醒的是哪一个线程, 也许我们需要唤醒的线程不能够被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒有所线程),线程被唤醒后会进入锁池,等待获取锁标 记。
﹒当线程调用stop方法,即可使线程进入消亡状态,但是由于stop方法是不安全的,不鼓励使用,大家可以通过run方法里的条件变通实现线程的 stop。
线程的优先级
1.线程的优先级及设置
线程的优先级是为了在多线程环境中便于系统对线程的调度,优先级高的线程将优先执行。
一个线程的优先级设置遵从以下原则:
线程创建时,子继承父的优先级。
线程创建后,可通过调用setPriority()方法改变优先级。
线程的优先级是1-10之间的正整数。
1- MIN_PRIORITY
10-MAX_PRIORITY
5-NORM_PRIORITY
如果什么都没有设置,默认值是5。
但是不能依靠线程的优先级来决定线程的执行顺序。
2.线程的调度策略
线程调度器选择优先级最高的线程运行。但是,如果发生以下情况,就会终止线程的运行:
线程体中调用了yield()方法,让出了对CPU的占用权。
线程体中调用了sleep()方法,使线程进入睡眠状态。
线程由于I/O操作而受阻塞。
另一个更高优先级的线程出现。
在支持时间片的系统中,该线程的时间片用完。