在上文中我们分析了java内存模型和volatile、synchronized的实现原理。在这篇文章中,我们继续分析一下Thread的常用api有哪些,以及它们是如何实现的。
Thread的结构
我们先通过源码看看Thread中主要封装了哪些对象:
private char name[]; //线程的名称
private int priority; //线程的优先级
private boolean daemon = false; //该线程是否为守护线程,默认为false
private Runnable target; //该线程要执行的任务
private ThreadGroup group; //该线程归属的线程组
private ClassLoader contextClassLoader; //加载器,默认为AppClassLoader
private static int threadInitNumber; //数字标识匿名线程
ThreadLocal.ThreadLocalMap threadLocals = null; //线程私有变量
private long stackSize; //分配给线程的栈空间大小,默认0(不限制)
private long tid; //线程id
private static long threadSeqNumber; //线程序列
private volatile int threadStatus = 0; //线程运行状态,默认是0(NEW)
volatile Object parkBlocker; //java.util.concurrent.locks.LockSupport.park使用
private volatile Interruptible blocker; //枚举字段,interrupt方法使用
private final Object blockerLock = new Object(); //更改blocker值时使用,作为锁的对象
Thread的构造方法
其完整的构造方法声明如下:
public Thread(ThreadGroup group, Runnable target, String name, long stackSize);
其中,ThreadGroup如果不指定,则为当前线程(即启动该线程的线程)的ThreadGroup;Runnable就是该线程将要执行的类,分两种情况,一种是继承了Thread类,一种是实现了Runnable接口;name就是线程的名称,如果不指定则为"Thread-索引号(索引从0开始)";stackSize是JVM分配给线程的栈空间大小,默认0(不限制)。
Thread关于自身信息的API
setName(String name)/getName()
读写线程名称
getThreadGroup()
读线程组
activeCount()
获取当前线程所在线程组上的存活线程
setDaemon(boolean on)
设置当前线程为守护线程。守护线程不应执行重要的任务,一般用于记录日志或监控之用,因为jvm当只有守护线程在运行时就停止
isDaemon()
判断该线程是否是守护线程
checkAccess()
判断当前线程是否有权限去操作该线程
holdsLock(Object obj)
判断该线程是否持有某对象的锁
getId()
获取当前jvm中该线程的唯一标示
isAlive()
判断该线程是否存活,标准是线程已启动但还没有执行完毕
getState()
获取该线程状态,返回值为枚举,包括NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED
Thread关于执行的API
start()
正确的启动一个线程的方法。先完成一些初始化动作,然后会通知JVM调用线程的run()方法
run()
只是单纯地执行线程代码,并没有启动一个新的线程。
join(long millis, int nanos)
保证指定时间以内,在JVM中只有该线程在运行。millis是等待的毫秒数,nanos是微秒数(参数可省)。如果millis填0,则一直等到当前线程执行完毕,JVM才会调度其他线程执行。
exit()
让该线程运行结束之前做下自身资源清理工作
yield()
当该线程让出CPU,给其他线程一个获取调度的机会,但是不确保一定会调度到其他线程上。
sleep(long millis, int nanos)
让该线程在指定时间以内休眠,需要注意的是在休眠过程中该线程不会释放持有的锁。millis是等待的毫秒数,nanos是微秒数(参数可省)。
interrupt()
它不会中断一个正在运行的线程,实际上是在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被wait、join或sleep阻塞时,那么它将接收到一个中断异常(InterruptedException),我们可以在异常块里调用本方法,从而提早地终结被阻塞状态。如果线程没有被阻塞,这时调用interrupt()将不起作用,仅仅是设置中断标志位为true的情况
interrupted()
判断该线程中断标识是否为true,返回该标识并将标识置为false
isInterrupted()
判断该线程中断标识是否为true,返回该标识。其实该方法和interrupted()方法底层调用的都是isInterrupted(boolean isClearState),只不过参数不同而已
Thread API中不鼓励使用的方法
setPriority(int newPriority)/getPriority()
读写线程的优先级。关于优先级有三个枚举值:
MIN_PRIORITY = 1;
NORM_PRIORITY = 5;
MAX_PRIORITY = 10;
数据越大,优先级越高。但是这个方法不推荐使用,是因为并不是所有的JVM都会严格按照该优先级的设定而调度线程,而且在有的JVM中,当一个线程的优先级设置过高会导致只运行该线程,在该线程运行完之前不会调度到其他线程上
suspend()/resume()
暂停/唤醒该线程。也不推荐使用,因为暂停并不释放锁,可能导致死锁;而且暂停的线程状态仍然是RUNNABLE,排查问题很难
destroy()
销毁该线程,但没有进行清理工作。线程持有的锁(monitor)不会释放
stop(Throwable obj)
停止线程,但没有进行清理工作。线程持有的锁(monitor)不会释放
下篇文章:java多线程解说【肆】_锁实现:wait()/notify()