程序猿学社的GitHub,欢迎Star
https://github.com/ITfqyd/cxyxs
本文已记录到github,形成对应专题。
前言
听过前面几篇文章的阅读,我们对多线程已经有了大致的了解。世界上的万物都是有规律的,人有生命周期,幼儿期->少年->青年,当然线程也不例外。跟着社长一起来看看线程的生老病死是怎么一回事。
1.通过查看源码了解线程的生命周期
首先先敲出Thread,查看该类的源码,通过Alt+7(idea)查看类的所有方法。
通过该类我们可以发现有一个State的枚举类。
/**
* A thread state. A thread can be in one of the following states:
* <ul>
* <li>{@link #NEW}<br>
* A thread that has not yet started is in this state.
* </li>
* <li>{@link #RUNNABLE}<br>
* A thread executing in the Java virtual machine is in this state.
* </li>
* <li>{@link #BLOCKED}<br>
* A thread that is blocked waiting for a monitor lock
* is in this state.
* </li>
* <li>{@link #WAITING}<br>
* A thread that is waiting indefinitely for another thread to
* perform a particular action is in this state.
* </li>
* <li>{@link #TIMED_WAITING}<br>
* A thread that is waiting for another thread to perform an action
* for up to a specified waiting time is in this state.
* </li>
* <li>{@link #TERMINATED}<br>
* A thread that has exited is in this state.
* </li>
* </ul>
*
* <p>
* A thread can be in only one state at a given point in time.
* These states are virtual machine states which do not reflect
* any operating system thread states.
*
* @since 1.5
* @see #getState
*/
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
/**
* Returns the state of this thread.
* This method is designed for use in monitoring of the system state,
* not for synchronization control.
*
* @return this thread's state.
* @since 1.5
*/
public State getState() {
// get current thread state
return sun.misc.VM.toThreadState(threadStatus);
}
getState方法获取当前线程的状态。
通过这个枚举类,我们可以看出线程的生命周期有6种
- NEW(新建)
- RUNNABLE(就绪)
- BLOCKED(阻塞)
- WAITING(等待)
- TIMED_WAITING(限时等待)
- TERMINATED(死亡或者完成)
2.通过代码更好的理解线程6种周期状态
NEW(新建)
单单创建一个线程,不运行,就是线程的新建状态。
public class New {
public static void main(String[] args) {
Thread t = new Thread();
System.out.println(t.getState());
}
}
RUNNABLE(就绪)
一个线程调用start方法后,表示他已经在jvm中运行了,还没有被cpu资源调度。就称之就绪状态。
package com.cxyxs.thread.five;
/**
* Description:转发请注明来源 程序猿学社 - https://ithub.blog.csdn.net/
* Author: 程序猿学社
* Date: 2020/2/22 11:08
* Modified By:
*/
public class RUNNABLE {
public static void main(String[] args) {
Thread t = new Thread();
t.start();
System.out.println(t.getState());
}
}
TERMINATED(死亡或者完成)
package com.cxyxs.thread.five;
/**
* Description:转发请注明来源 程序猿学社 - https://ithub.blog.csdn.net/
* Author: 程序猿学社
* Date: 2020/2/22 11:24
* Modified By:
*/
public class TERMINATED {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread();
t.start();
Thread.sleep(5000);
System.out.println(t.getState());
}
}
结合上RUNNABLE就绪状态的代码和结果,各位社友,觉得为什么我增加一句Thread.sleep后,代码的状态就发生改变咯?
首先回答这个问题之前,我们需要了解时间片的概念
我们调用线程的start方法以后,不是马上就执行,而是需要通过cpu的资源调度,假设,只有一个cpu,一个通道,同时存在多个线程,因为同一时间,只可能有一个线程抢到资源,cpu会给这个线程分配一个时间片,如果在固定时间内,还没有处理完,为了保证公平公正的原则,cpu会切下一个线程,在固定时间内处理完,也会切下一个线程。这样就不会造成资源的浪费。
- 调用start方法以后,我延迟5秒钟,就是保证线程已经跑完了,所以最终的结果为TERMINATED
BLOCKED(阻塞)
当前线程在等待一个monitor lock的过程。例如执行同步代码块或者同步方法,都会导致线程处于堵塞状态。
package com.cxyxs.thread.five;
/**
* Description:转发请注明来源 程序猿学社 - https://ithub.blog.csdn.net/
* Author: 程序猿学社
* Date: 2020/2/22 14:13
* Modified By:
*/
public class BLOCKED {
public static synchronized void test() throws Exception{
//模拟业务
Thread.sleep(3000);
}
public static void main(String[] args) throws Exception{
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
test();
} catch (Exception e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread thread1= new Thread(runnable);
thread1.start();
Thread.sleep(100);
System.out.println(thread1.getState());
}
}
这段代码就两处关键的步骤
- 第一步,启动一个同步方法,就是相当于上锁,多个线程访问时,需要拿到锁的资格后,才能进行访问该访问。
- 第二步 休眠100毫秒,这个代码很关键,就是为了保证线程已经开始运行了。
案例:
隔壁小王家就一个茅坑,小王进去后,就直接把茅坑的们反锁勒,其他的人,需要等小王用完后,才能进入。其他的人就堵塞在这里。
下面几种方法会导致阻塞
- 同步方法或者同步代码块
- Object.wait
WAITING(等待)
一个线程在等待另一个线程被唤醒,这个状态就是等待。
package com.cxyxs.thread.five;
/**
* Description:转发请注明来源 程序猿学社 - https://ithub.blog.csdn.net/
* Author: 程序猿学社
* Date: 2020/2/22 14:46
* Modified By:
*/
public class WAITING {
public static void main(String[] args) throws Exception{
Object locker = new Object();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
synchronized (locker){
System.out.println("调用wait");
locker.wait();
System.out.println("调用wait后");
}
System.out.println("调用wait业务");
} catch (Exception e) {
e.printStackTrace();
}
}
};
Thread t = new Thread(runnable);
t.start();
Thread.sleep(100);
runnable = new Runnable() {
@Override
public void run() {
try {
synchronized (locker){
System.out.println("调用notify");
locker.notify();
System.out.println("调用notify后");
}
System.out.println("调用notif业务");
} catch (Exception e) {
e.printStackTrace();
}
}
};
Thread t1 = new Thread(runnable);
t1.start();
System.out.println(t.getState()+","+t1.getState());
}
}
通过线程t1唤醒线程t,休眠100毫秒,是为了保证线程已经运行起来,线程t调用wait,表示线程进入线程等待队列,当前这个线程t会堵塞在这里,不会进行向下运行,需要另外一个线程把他唤醒,同步锁是locker ,获取锁以后,调用notify,唤醒线程t.
可能导致WAITING的几条
- Object.wait
- Thread.join
- LockSupport.park
通过Object.notify进行唤醒
TIMED_WAITING(限时等待)
一个线程在一个特定的时间内等待另一个线程完成会有这么一个状态,自动唤醒,称为限时等待。
package com.cxyxs.thread.five;
/**
* Description:转发请注明来源 程序猿学社 - https://ithub.blog.csdn.net/
* Author: 程序猿学社
* Date: 2020/2/22 13:42
* Modified By:
*/
public class TIMEDWAITING{
public static void main(String[] args) throws Exception{
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
//1
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
//2
Thread.sleep(100);
System.out.println(thread.getState());
}
}
第一步,延迟5秒,就是为了让出资源
第二步,延迟100豪秒,就是为了保证线程已经在执行状态。并保证这个线程抢到时间片。
案例:
隔壁老王打算去上班,在等待公交车,等了半天,发现没有座位,想了想,还是没有上车,最后,等了下一班车,再去的公司。
实际上,就是我已经在cpu资源里面拿到时间片资格了,但是,由于这个同学,十分的懒,想要舒服一点,所以他就把这个上车的资格让给了其他人。当然,最后,小王也顺利的到达勒公司。
下面几种方法会导致限制等待
- Thread.sleep
- Object.wait
- Thread.join
- LockSupport.parkNanos
- LockSupport.parkUntil
6种状态总结
面试遇到问生命周期,可以从这两张图开始说起。先从下面这个图开始,再到第一个图。先从大的方向开始说起,再到底层源码是怎么样实现的。
后记
下篇文章,会结合实现的demo,了解Thread中一些常用方法。
转载:https://blog.csdn.net/qq_16855077/article/details/104439223