专栏文章:操作系统
作者主页:https://blog.csdn.net/2301_77611317?type=blog
文章目录
🦝前言
Java中线程的状态分为6种:初始状态(NEW)、就绪状态(RUNNABLE)、等待状态(WAITING)、超时等待(TIMED_WAITING)、阻塞状态(BLOCKED)、终止状态(TERMINATED)
这6种状态之间也存在着某种转换方式。
🐱一、初始状态(NEW)
初始状态下,系统已经创建好Thread对象,线程任务也相应地被确立,但是线程并没有被启动。
public static void main(String[] args) throws InterruptedException {
Thread main=Thread.currentThread();
Thread t1 =new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
System.out.println("t1线程的状态:"+t1.getState());
t1.start();
t1.join();
}
运行结果:
t1线程的状态:NEW
🐱二、就绪状态(RUNNABLE)
就绪状态一般分为两个子状态:运行状态(RUNNING)、准备状态(READY),但是在Java中RUNNABLE 兼并两个子状态的属性
-
运行状态:线程正在CPU上运行。
-
准备状态:线程不在运行,处于准备随时被CPU调度运行的状态。
public static void main(String[] args) throws InterruptedException { Thread main=Thread.currentThread(); Thread t1 =new Thread(()->{ try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } }); t1.start(); System.out.println("t1线程的状态:"+t1.getState()); t1.join(); }
运行结果:
t1线程的状态:RUNNABLE
🐱三、等待状态(WAITING)
等待状态下,线程没有等待时限,会无限期地进行等待,直到被notify或者notifyAll方法唤醒。
public static void main(String[] args) throws InterruptedException {
Thread main=Thread.currentThread();
Thread t1 =new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//在t1线程终止之前,main线程将一直处于WAITING状态!
System.out.println("main线程的状态:"+main.getState());
});
t1.start();
t1.join();
}
运行结果:
main线程的状态:WAITING
🐱四、超时等待(TIMED_WAITING)
与等待状态相反,超时等待有一定的等待时限,时限一过线程会自动唤醒。
public static void main(String[] args) throws InterruptedException {
Thread t1 =new Thread(()->{
try {
//t1进入TIMED_WATING状态,1000毫秒之后自动唤醒
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
t1.start();
Thread.sleep(10);
System.out.println("t1线程的状态:"+t1.getState());
t1.join();
}
运行结果:
t1线程的状态:TIMED_WAITING
🐱五、阻塞状态(BLOCKED)
当进入到synchronized关键字修饰的方法或者代码块时,线程处于阻塞状态。通常认为,此状态与上文提到的WAITING状态和TIMED_WAITING状态都属于阻塞,只不过进入这三种状态的方式不同。
1、synchronized关键字修饰的代码块
//创建“锁”对象
Object locker= new Object();
Thread t1 =new Thread(()->{
System.out.println("线程1执行");
Thread T1=Thread.currentThread();
//synchronized修饰代码块,括号内填"锁"对象名 -->locker
synchronized (locker){
System.out.println("线程1拿到“锁”后的状态:"+T1.getState());
}
});
2、synchronized关键字修饰的方法
//synchronized关键字修饰的方法
public static synchronized void func(){
System.out.println("线程1拿到“锁” ");
}
public static void main(String[] args) throws InterruptedException{
Thread t1 =new Thread(()->{
System.out.println("线程1执行");
func(); //调用被synchronized修饰的func方法
});
t1.start();
}
🐱六、终止状态(TERMINATED)
终止状态下,内核中线程已经被销毁,但Thread对象还存在。
当线程run方法或者lambda表达式执行完毕,或者主线程main执行完毕时,线程就会进入终止状态。
public static void main(String[] args) throws InterruptedException {
Thread t1 =new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
t1.start();
t1.join();
//在t1终止之后查询状态
System.out.println("t1线程的状态:"+t1.getState());
}
运行结果:
t1线程的状态:TERMINATED
🐱七、线程状态之间的转换
1、处于NEW状态下的线程,在主线程或其他线程中调用start()后转换为RUNNABLE状态。
public static void main(String[] args) {
Thread t1= new Thread(()->{
System.out.println("线程1执行");
});
//NEW状态
System.out.println("线程1状态:"+t1.getState());
//t1线程转为RUNNABLE状态
t1.start();
//RUNNABLE状态
System.out.println("线程1状态:"+t1.getState());
}
运行结果:
线程1状态:NEW
线程1状态:RUNNABLE
2、处于RUNNABLE状态下的线程,调用wait或者join方法都会进入WAITING状态。
- join方法:举个栗子,在主线程中调用t1.join()等待t1,主线程进入WAITING状态。
//在主线程中调用join方法等待t1线程
public static void main(String[] args) throws InterruptedException{
Thread main=Thread.currentThread();
Thread t1 =new Thread(()->{
System.out.println("线程1执行");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("调用join后,main线程状态:"+main.getState());
});
t1.start();
//RUNNABLE状态
System.out.println("调用join前,main线程状态:"+main.getState());
//调用join 使main线程转换WAITING状态
t1.join();
}
运行结果:
线程1执行
调用join前,main线程状态:RUNNABLE
调用join后,main线程状态:WAITING
-
wait方法:这个方法一般用在syncronized修饰的代码块或方法里。
调用wait会发生两个事件:(1)线程被解锁 “锁” 对象 (2)被解锁的线程将会进入WAITING状态。
注意:(1)调用的是“锁”对象的wait! (2)必须要抛出InterruptedException异常(try/catch语句或者throws)
代码示例:
public static void main(String[] args) throws InterruptedException{
Object locker=new Object();
Thread t1= new Thread(()->{
System.out.println("线程1执行");
synchronized (locker){
try {
//解锁后 t1进入WAITING状态
locker.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1线程被唤醒");
}
});
t1.start();
System.out.println("线程1 wait之前的状态:"+t1.getState());
Thread.sleep(100);
System.out.println("线程1 wait之后的状态:"+t1.getState());
//注意notify必须在“锁”里被调用
synchronized (locker) {
//唤醒t1
locker.notify();
}
}
运行结果:
线程1执行
线程1 wait之前的状态:RUNNABLE
线程1 wait之后的状态:WAITING
t1线程被唤醒
当然,wait也有重载,其参数列表与sleep相同(sleep看这篇文章),值得注意的是,wait如果加了时间参数,t1线程解锁后不会进入WAITING,取而代之的是TIMED_WAITING;不需要调用notify方法唤醒线程,过了时间线程自动唤醒。
public static long start=0,end=0;
public static void main(String[] args) throws InterruptedException{
Object locker=new Object();
Thread t1= new Thread(()->{
System.out.println("线程1执行");
synchronized (locker){
try {
//wait前的时间戳
start=System.currentTimeMillis();
//解锁后 t1进入WAITING状态
locker.wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//wait后的时间戳
end=System.currentTimeMillis();
System.out.println("t1线程被唤醒");
System.out.println("wait的等待时间:"+(end-start));
}
});
t1.start();
System.out.println("线程1 wait之前的状态:"+t1.getState());
Thread.sleep(100);
System.out.println("线程1 wait之后的状态:"+t1.getState());
//注意不需要手动唤醒wait
// synchronized (locker) {
// locker.notify();
// }
t1.join();
}
运行结果:
线程1执行
线程1 wait之前的状态:RUNNABLE
线程1 wait之后的状态:TIMED_WAITING
t1线程被唤醒
wait的等待时间:1012 //理论是等待1秒,但是由于唤醒后线程并不会立马进入RUNNING状态
3、当只存在一个“锁”对象,又有两个线程需要获取"锁"对象时,其中一个线程抢到锁,另一个线程则会进入BLOCKED状态;只有当前者释放了锁,后者才会退出BLOCKED状态。一般将这种事件成为“锁竞争”。
代码示例:
public static void main(String[] args) throws InterruptedException{
Object locker=new Object();
Thread main=Thread.currentThread();
Thread t1 =new Thread(()->{
synchronized (locker){
System.out.println("t1线程抢到锁了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
Thread t2 =new Thread(()->{
Thread T2=Thread.currentThread();
synchronized (locker){
System.out.println("t2线程抢到锁了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t1.start();
t2.start();
Thread.sleep(10);
if (t1.getState() == Thread.State.BLOCKED) System.out.println("t1线程状态:" + t1.getState());
if (t2.getState() == Thread.State.BLOCKED) System.out.println("t2线程状态:" + t2.getState());
t1.join();
t2.join();
}
运行结果:
t1线程抢到锁了
t2线程状态:BLOCKED
t2线程抢到锁了
🦄总结
Java线程状态分为六种:NEW(初始状态)、RUNNABLE(就绪状态)、WAITING(等待状态)、TIMED_WAITING(超时等待)、BLOCKED(阻塞状态)、TERMINATED(终止状态)。
- NEW转为RUNNABLE调用start方法;
- RUNNABLE转WAITING可调用join或者wait两种无参数的方法;转TIMED_WAITING则调用有参数的方法;转BLOCKED则需要触发锁竞争的事件;
- 调用notify或者notifyAll方法可以消除WAITING状态;而TIMED_WAITING状态下的线程超时会自动唤醒。
文章到这结束啦,感谢阅读~
如果你觉得文章写的还不错,记得点赞收藏评论三连~ ❤