1. 线程状态
public enum State {
NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED;
}
说明:
状态 | 描述 |
---|---|
NEW |
至今尚未启动的线程的状态 |
RUNNABLE |
可运行线程的线程状态 |
BLOCKED |
受阻塞并且正在等待监视器锁的某一线程的线程状态 |
WAITING |
某一等待线程的线程状态 |
TIMED_WAITING |
具有指定等待时间的某一等待线程的线程状态 |
TERMINATED |
已终止线程的线程状态。线程已经结束执行 |
如图:
2. 主要属性
/*
* 线程名字,通过构造参数来指定
*/
private volatile String name;
/*
* 表示线程的优先级,优先级越高,越优先被执行(最大值为10,最小值为1,默认值为5)
*/
private int priority;
/*
* 线程是否是守护线程:当所有非守护进程结束或死亡后,程序将停止
*/
private boolean daemon = false;
/*
* 将要执行的任务
*/
private Runnable target;
/*
* 线程组表示一个线程的集合。此外,线程组也可以包含其他线程组。线程组构成一棵树,在树中,除了初始线程组外,每个线程组都有一个父线程组。
*/
private ThreadGroup group;
/*
* Thread ID
*/
private long tid;
/*
* 用来生成Thread ID使用
*/
private static long threadSeqNumber;
/*
* 第几个线程,在init初始化线程的时候用来赋给thread.name
*/
private static int threadInitNumber
/*
* 线程从创建到最终的消亡,要经历若干个状态。
* 一般来说,线程包括以下这几个状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)
*/
private volatile int threadStatus = 0;
3. 构造函数
源码
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(String name) {
init(null, null, name, 0);
}
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name) {
init(group, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
}
构造函数中第三个参数为线程名称,而"Thread-" + nextThreadNum()
是系统默认的线程名,会将属性中的threadInitNumber
加一。
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
4. 启动线程
4.1 start()
方法
源码
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0){// 如果状态不是`NEW`
throw new IllegalThreadStateException();
}
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
4.2 run
方法
源码
public void run() {
if (target != null) {
target.run();
}
}
5. 守护线程与非守护线程(用户线程)
守护线程与普通线程的唯一区别是:
任何一个守护线程都是整个JVM中所有非守护线程的保姆
守护线程与main同生共死,当main退出,它将终止,而普通线程是在任务执行结束才停止。
Java虚拟机在它所有非守护线程已经离开后自动离开。守护线程则是用来服务用户线程的,如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去。
示例
public class ThreadDemo extends Thread {
public void run() {
while (true) {
for (int i = 1; i <= 100; i++) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Thread daemonThread = new Thread(new ThreadDemo());
daemonThread.setName("测试thread");
// 设置为守护进程
daemonThread.setDaemon(true);
daemonThread.start();
System.out.println("isDaemon = " + daemonThread.isDaemon());
Thread t = new Thread(new ThreadDemo());// 为非守护线程
t.start();
}
}
因为有线程t 的存在,守护线程daemonThread 一直执行。
public static void main(String[] args) {
Thread daemonThread = new Thread(new ThreadDemo());
daemonThread.setName("测试thread");
// 设置为守护进程
daemonThread.setDaemon(true);
daemonThread.start();
}
如果只有守护线程daemonThread
,程序因为main
线程的退出而退出。
注意点:
thread.setDaemon(true)
必须在thread.start()
之前设置,否则会跑出一个IllegalThreadStateException
异常。你不能把正在运行的常规线程设置为守护线程。在Daemon线程中产生的新线程也是Daemon的
不要认为所有的应用都可以分配给Daemon来进行服务,比如读写操作或者计算逻辑
6. 主要方法
6.1 sleep(long millis)
源码
public static native void sleep(long millis) throws InterruptedException;
注意点:
sleep是指线程被调用时,占着CPU不工作,形象地说明为“占着CPU睡觉”,此时,系统的CPU部分资源被占用,其他线程无法进入。
sleep方法不会释放锁。
示例:
public class ThreadDemo extends Thread {
private static Long i = 0L;
public void run() {
while (true) {
synchronized (ThreadDemo.class) {
i++;
try {
sleep(1000);
} catch (InterruptedException e) {
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t = new ThreadDemo();
t.start();
Thread t1 = new ThreadDemo();
t1.start();
}
}
两个线程(A、B)同时执行,当线程A获得同步锁并进入同步块时,线程B会处于BLOCKED
状态等待锁。而当线程A执行sleep
后,线程A状态为TIMED_WAITING
,而由于sleep
不会释放锁,所以线程B的状态不会改变。
6.2 yield()
源码
public static native void yield();
注意点:
调用yield方法会让当前线程交出CPU权限,让拥有相同优先级的线程有获取CPU执行时间的机会
调用yield方法并不会让线程进入
BLOCKED
状态,而是让线程重回RUNNABLE
状态, 但不能保证迅速转换。不会释放锁
6.3 isAlive()
表示线程当前是否为可用状态,如果线程已经启动,并且没有死亡,则返回true,否则为false
源码
public final native boolean isAlive();
6.4 join()
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法
源码
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
public final void join() throws InterruptedException {
join(0);
}
注意点:
join
方法是通过wait
方法实现的,所以会释放对象锁。调用方线程状态为
WAITING
。join
需要在start
之后调用
示例一
public class ThreadDemo extends Thread {
private static Long i = 0L;
public void run() {
synchronized (this) {
while (true) {
i++;
try {
sleep(1000);
} catch (InterruptedException e) {
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t = new ThreadDemo();
t.start();
Thread.sleep(1000);
t.join();
}
}
此处需要注意的是,此处t.join()
不会被执行,main
线程会一直等待对象锁而BLOCKED
。
6.5 interrupt()
Thread.sleep
、Thread.join
、Object.wait
等在检查到线程的中断状态时,会抛出InterruptedException,同时会清除线程的中断状态.
在java的线程Thread类中有三个方法,比较容易混淆,在这里解释一下:
interrupt
:设置线程的中断状态isInterrupt
:线程是否中断,不会清楚中断状态。interrupted
:返回线程的上次的中断状态,并清除中断状态。
示例
public class ThreadDemo extends Thread {
public void run() {
System.out.println("即将睡眠");
try {
Thread.sleep(1000 * 4);
} catch (InterruptedException e) {
System.out.println("被唤醒");
}
System.out.println("处理其他事情");
}
public static void main(String[] args) throws Exception {
Thread t = new ThreadDemo();
t.start();
Thread.sleep(1000);
t.interrupt();
}
}
注意点:
Thread.interrupt()
方法不会中断一个正在运行的线程如果线程在调用
Object
类的wait()
、wait(long)
或wait(long, int)
方法,或者该类的join()
、join(long)
、join(long, int)
、sleep(long)
或sleep(long, int)
方法过程中受阻,则其中断状态将被清除,它还将收到一个InterruptedException
异常。synchronized
在获锁的过程中是不能被中断的,意思是说如果产生了死锁,则不可能被中断