线程使用方式
JDK线程的顶层抽象是Runnable接口,本质上,线程相关的类都是基于Runnable和Thread实现类实现。
JDK API级别有不同的创建线程的方式,但本质是还是基于Runnable接口和Thread类两种方式:
// 继承Thread类public class ThreadOne extends Thread{ @Override public void run(){ // TODO: do something }}ThreadOne t = new ThreadOne();t.start();// 实现Runnable接口public class ThreadTwo implements Runnable{ @Override public void run(){ // TODO: do something }}Thread t = new Thread(new ThreadTwo());t.start();
Java中的线程状态
// java.lang.Threadpublic enum State { // 新建状态:线程尚未启动的状态 NEW, // 可运行状态:RUNNABLE状态的线程在JVM层面正在执行,但在操作系统层面也可能处于等待一些系统资源的状态,比如CPU. // RUNNING和READY两种状态: RUNNING: 线程处于运行状态,正在占用CPU时间片; READY:线程准备就绪,只需等待分配CPU即可执行 RUNNABLE, // 阻塞状态:阻塞状态的线程在阻塞等待获取对象的监视器锁,以进入同步代码块或同步方法。BLOCKED, // 等待状态:处于等待状态的线程需要等待其他线程执行特定动作 WAITING, // 超时等待状态:线程等待状态带有一定超时时间 TIMED_WAITING, // 终止状态:线程执行完成,或线程中断 TERMINATED;}
线程状态转换
JDK不同线程状态间的转换参考下面的转换图。
重点说明如下几个状态:
RUNNABLE
程序调用Thread类的start()方法后线程进入可运行状态,从底层操作系统的角度看,线程在RUNNABLE状态可能正在运行,也可能处于就绪状态。二者区别是:线程只有获取CPU时间分片后才能真正执行,在READY状态下,线程未获取CPU时间片,所以处于一种非运行状态。而在RUNNING状态则真正是占有CPU执行计算。
RUNNING和READY两个状态的切换来自于系统调度和主动调用Thrad.yield()方法让出CPU。需要注意的是,让出CPU使用权并不意味着释放线程所持有的资源,比如对象锁。
BLOCKED:阻塞状态
线程进入阻塞状态的原因是:尝试获得对象监视器锁进入同步代码时失败而阻塞。
WAITING:
代码调用wait()/join()/park()方法时当前线程会立刻释放锁资源并进入等待状态,且必须等待其他线程的唤醒动作才能重新返回的RUNNABLE状态。
线程优先级
JDK中定义了从1到10共十个线程优先级,参考源码:
// 最小优先级public final static int MIN_PRIORITY = 1;// 默认优先级public final static int NORM_PRIORITY = 5;// 最大优先级public final static int MAX_PRIORITY = 10;// Thread类中的优先级设置public final void setPriority(int newPriority) { ThreadGroup g; checkAccess(); if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } // 获取所属线程组,如果为空则获取父线程组parent.getThreadGroup(); if((g = getThreadGroup()) != null) { if (newPriority > g.getMaxPriority()) {newPriority = g.getMaxPriority(); } // 调用本地方法:设置线程优先级 setPriority0(priority = newPriority); }}
JDK定义的线程优先级可能与操作系统线程优先级不一致,优先级高的线程不一定比优先级低的线程优先执行,所以,不要绝对依赖线程优先级来指定不同线程的执行顺序。常见的操作系统与Java线程优先级映射关系:
Daemon线程
Java线程可以分为用户线程和守护线程,相对于用户线程而言,守护线程像是后端服务。当所有用户线程退出后JVM退出。Thread类源码中定义了布尔型变量daemon标识是否是守护线程。