线程的生命周期
- 新建状态(new):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread()
- 就绪状态(Runnable):当调用线程对象的start()方法,线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行
- 运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中。
- 阻塞状态(blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
- 等待阻塞 – 运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
- 同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态
- 其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态
- 死亡状态(Dead): 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
创建线程的几种方式
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
- 线程池 或Future (FutureTask)
线程之间的调度和通信
- Join()
线程加入,从当前线程中加入一个线程,当前线程等待加入的线程执行完毕在执行;即在当前线程调用另一个线程的join()方法,则当前线程处于阻塞状态,
等待另一个线程执行完毕后 当前线程从阻塞状态转入就绪状态等待cpu的调度
join()是Thread的一个方法
public final synchronized void join(long millis)
- Sleep(long millis)
线程睡眠,让当前线程睡眠,从运行状态转入到阻塞状态;注意:sleep方法不会释放锁public static native void sleep(long millis) throws InterruptedException;
- yield()
线程让步,让当前线程停止,由运行状态转入到就绪状态从新等待cpu的调度,yield方法不会释放锁
public static native void sleep(long millis) throws InterruptedException;
- interrupt()
线程中断,中断当前线程 设置中断线程标记 为true;
如果当前线程处于阻塞状态,例如调用 wait, join, sleep等阻塞方法 如果中断线程会抛异常InterruptedException
如果当前线程在进行io操作,中断线程会抛ClosedByInterruptException
通过线程的中断标志来控制线程 例如
while(!isInterrupted()) {
...
}
- wait()
使当前线程处于等待阻塞状态,直到其他线程调用该同步锁对象的notify() 或者notifyAll() 方法唤醒
public final void wait() throws InterruptedException
当前线程必须拥有该对象的监视器; 就是说当前线程必须处于线程同步中,或者说处于synchronized 修饰的方法或代码块中
当调用wait方法的时候,当前线程会处于阻塞状态,释放此对象的监视器所有权,即释放锁,等待另一个线程唤醒 从新获取锁 并继续执行
notify()/notifyAll()
public final native void notify();
和wait一样 必须处于同步代码中;
唤醒和当前线程使用相同的对象锁 并 处于wait()的一个线程,但是 并不释放锁,知道线程结束 才会释放锁;
notifyAll() 则是唤醒所有;
线程优先级
java中的线程优先级的范围是1~10,默认的优先级是5。每个线程默认的优先级都与创建它的父线程具有相同的优先级。默认情况下,mian线程具有普通优先级。“高优先级线程”会优先于“低优先级线程”执行。Thread提供了setPriority(int newPriority)和getPriority()方法来设置和返回线程优先级。
守护线程
java 中有两种线程:用户线程和守护线程。可以通过isDaemon()方法来区别它们:如果返回false,则说明该线程是“用户线程”;否则就是“守护线程”。通过setDaemon(true) 来设置为守护线程;用户线程一般用户执行用户级任务,而守护线程也就是“后台线程”,一般用来执行后台任务。需要注意的是:Java虚拟机在“用户线程”都结束后会后退出。