线程的生命周期
传统线程模式的线程状态
1.新建状态:创建线程对象
2.就绪状态:调用start()方法后,有执行资格没有执行权(抢夺cpu执行权)
3.运行状态:抢到了cpu执行权,运行代码,当cpu执行权被抢走,又回到就绪状态
4.死亡状态:当run方法的内容全部执行完,线程就会死亡,变成垃圾
5.阻塞状态:当遇到sleep或者其他阻塞方法,线程就会等着,没有执行资格也没有执行权
,当sleep的时间到了或者其他阻塞方式结束,又会回到就绪状态。
在
java.lang.Thread
类内部定义了一个枚举类用来描述线程的六种状态:public enum State { NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED; }
跟传统线程模型中的线程状态不同的是:
- 枚举类中没有区分
就绪
和运行
状态,而是定义成了一种状态Runnable
。
- 因为对于Java对象来说,只能标记为可运行,至于什么时候运行,不是JVM来控制的了,是OS来进行调度的,而且时间非常短暂,因此对于Java对象的状态来说,无法区分。只能我们人为的进行想象和理解。
- 传统模型中的阻塞状态在枚举类的定义中又细分为了三种状态的:
BLOCKED
、WAITING
、TIMED_WAITING
。
BLOCKED
:是指互有竞争关系的几个线程,其中一个线程占有锁对象时,其他线程只能等待锁。只有获得锁对象的线程才能有执行机会。TIMED_WAITING
:当前线程执行过程中遇到Thread类的sleep或join,Object类的wait,LockSupport类的park方法,并且在调用这些方法时,设置了时间,那么当前线程会进入TIMED_WAITING,直到时间到,或被中断。WAITING
:当前线程执行过程中遇到遇到Object类的wait,Thread类的join,LockSupport类的park方法,并且在调用这些方法时,没有指定时间,那么当前线程会进入WAITING状态,直到被唤醒。
- 通过Object类的wait进入
WAITING
状态的要有Object的notify/notifyAll唤醒;- 通过Condition的await进入
WAITING
状态的要有Conditon的signal方法唤醒;- 通过LockSupport类的park方法进入
WAITING
状态的要有LockSupport类的unpark方法唤醒- 通过Thread类的join进入
WAITING
状态,只有调用join方法的线程对象结束才能让当前线程恢复;说明:当从
WAITING
或TIMED_WAITING
恢复到Runnable
状态时,如果发现当前线程没有得到监视器锁,那么会立刻转入BLOCKED
状态。
细节:
1.没给线程设置名字线程也有默认的名字格式Thread-x(x为序号,从0开始)
2.jvm虚拟机启动后会启动多条线程,其中有一条线程就是main线程,作用是调用main方法,并执行里面的代码
3.sleep方法:哪个线程执行到这个方法就会停留对应的时间
线程的调度
1.抢占式调度:多个线程抢占cpu的执行权,具有随机性
2.非抢占式调度:线程轮流执行
jvm中线程的调度是抢占式调度,优先级越高,抢到cpu的概率越大,Java中的线程分为10档,最低是1,不设置默认是5.
守护线程
fianal void setDaeom(boolean on)
细节:当非守护线程结束了,守护线程也会陆续结束
public static void main(String[] args) { //1.自己定义一个类继承Thread MyThread t=new MyThread(); MyThread1 t1=new MyThread1(); t.setName("线程1"); t1.setName("线程2"); t.setDaemon(true);//将线程1设置为守护线程 t.start(); t1.start(); } //线程1 class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+"hello"); } } //线程2 class MyThread1 extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName()+"hello"); } } }
细节:
1.我们可以看到,当非守护线程结束后,守护线程没有立马结束,会运行一小段时间,因为在线程退出的一瞬间,守护线程会抢到一小会cpu执行权
2.thread.setDaemon(true)必须在thread.start()之前设置,否则会抛出一个IllegalThreadStateException异常。
3.正在运行的常规线程不能设置为守护线程。
4.多个线程同时运行,守护线程会等所有线程都运行完停止。
礼让线程
public static void yield()
表示出让当前CPU执行权,从运行状态进入就绪状态
此方法跟sleep方法有点像
关于sleep()方法和yield()方的区别如下:
1.sleep方法暂停当前线程后,会进入阻塞状态,只有当睡眠时间到了,才会转入就绪状态;而yield方法调用后 ,是直接进入就绪状态,完全可能马上又被调度到运行状态。
2.sleep 方法会给其他线程运行的机会,但是不考虑其他线程优先级的问题;而yield 方法会优先给更高优先级的线程运行机会。
3.sleep方法声明抛出了InterruptedException,所以调用sleep方法的时候要捕获该异常,或者显示声明抛出该异常。而yield方法则没有声明抛出任务异常。
public class MyThread1 extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(i+getName()+"hello"); Thread.yield(); } } }
能尽量让执行结果均匀一点
插入线程
public final void join();
t.join(); for (int i = 0; i < 100; i++) { System.out.println(i+"主线程结束"); }
表示把t线程插入在当前线程之前完成。