声明:原创文章,转载请注明出处。//www.greatytc.com/p/4f9de47d1c43
一、线程生命周期
线程的状态有NEW、RUNNABLE、RUNNING、BLOCKED和TERMINATED五个状态。如下图所示:
- NEW:当我们new一个Thread对象后,在没有执行
start
方法之前,这仅仅是一个普通的Java对象。可以说线程处于NEW状态。 - RUNNABLE:当执行
start
方法后,JVM会创建一个线程并进入RUNNABLE状态,等待操作系统的调度。 - RUNNING:当操作系统调度处于RUNNABLE状态的线程成功后,此时该线程处于执行状态,并执行其逻辑代码。如果调用stop方法则进入TERMINATED状态;调用sleep/wait、获取锁资源或进行某个IO阻塞会进入BLOCKED状态;CPU时间片用完或者调用yield方法(放弃CPU执行),则会进入RUNNABLE状态。
- BLOCKED:阻塞状态。
- TERMINATED:终止状态。
二、start()和run()方法的区别
如下,我们新建一个线程,并执行start()
方法,使其进入RUNNABLE状态。然而我们重写的run()
方法却执行了,这是为什么?
public static void main(String[] args) {
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("hello world.");
}
};
t1.start();
}
我们看下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)
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 */
}
}
}
可以看到,start()
方法的核心为:start0()
方法。start0()
为native方法。在线程执行时,start0()
会调用run()
方法。
结论1:启动线程,应该执行start()
方法。如果调用run()
方法,有时会看到相同的执行结果,但实际上只是执行了Thread
对象的一个普通方法,并没有多线程运行。
结论2:从start()
的源码可以看到,如果执行两次start()
,会出现IllegalThreadStateException
。
结论3:如果一个线程的逻辑单元执行完,会进入TERMINATED状态,再次执行start()
也是不行的。通过上一节的状态转换图看到,从TERMINATED状态并不能回到RUNNABLE状态。
三、sleep()和yield()方法的区别
- yield():调用该方法会通知CPU调度器放弃CPU资源,线程从RUNINIG状态转到RUNNABLE状态。会导致线程上下文切换。但如果CPU资源不紧张,调度器会忽略yield请求。
- sleep():调用该方法会使得当前线程暂停指定的时间,线程从RUNNING状态转到BLOCKED状态。但不会释放锁资源。
四、join()方法
我们先从一个例子说起:
public class ThreadDemo {
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("t1 thread");
}
};
Thread t2 = new Thread() {
@Override
public void run() {
try {
t1.join();
} catch (Exception e) {}
System.out.println("t2 thread");
}
};
t1.start();
t2.start();
t2.join();
System.out.println("main thread");
}
}
t1 thread
t2 thread
main thread
无论输出多次其结果都是固定的t1→t2→main。如果去掉join()
方法,则三者的顺序则不固定。
当前线程Main执行中,join线程t2,则Main线程进入BLOCKED状态,直到t2结束生命周期或达到指定时间。同样地,t2线程执行中,join线程t1,则t2线程会等到t1线程执行完才会执行。
结论:当前执行线程join某个线程之后,该执行线程会等待join线程执行完/达到指定时间才会继续进行执行。常见的场景是一个任务分割成多个小任务,等各个小任务均执行完成后,再执行汇总操作。