【学习笔记】线程(四)之线程状态
-
创建状态:new一个Thread
-
就绪状态:调用start()
-
运行状态:cpu调用
-
阻塞状态:当调用sleep() wait() 或同步锁定时
-
死亡状态:正常执行完
停止线程
-
不推荐使用JDK 提供的 stop() destroy()方法【已废弃】
-
推荐线程自己停下来
-
建议使用一个标志位进行终止变量,当flag = false,则终止线程运行
package com.thread.state;
public class TestStop implements Runnable{
private boolean flag = true; //定义标志位
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("线程正在运行"+i++);
}
}
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
if (i==800){
testStop.stop();
System.out.println("线程该停止了");
}
System.out.println("main线程"+ i);
}
}
}
我们自己定义一个方法来转换标志位,用来停止线程
线程休眠
-
sleep(时间)指定当前线程阻塞的毫秒数
-
sleep存在异常 InterruptedException
-
sleep 时间达到后线程进入就绪状态
-
sleep可以模拟网络延时,倒计时等
-
每一个对象都有一个锁,sleep不会释放锁
模拟网络延时:
在之前买票的例子中,多个线程操作一个对象时,如果没有模拟延时,那么一个线程就把票全部拿完了,我们模拟延时,在拿票之前休眠1000ms,就不会出现上述问题
模拟网络延时的作用:放大问题的发生性
模拟倒计时:
10秒倒计时,通过sleep来控制中间间隔1秒
package com.thread.state;
public class TestSleep {
public static void main(String[] args) throws InterruptedException {
sleepTime();
}
public static void sleepTime() throws InterruptedException {
int num = 10;
while (true){
System.out.println(num--);
Thread.sleep(1000);
if (num<=0){
break;
}
}
}
}
打印当前的时间:
package com.thread.state;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestSleep {
public static void main(String[] args) throws InterruptedException {
Date date = new Date(System.currentTimeMillis()); //获取系统当前时间
while (true){
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
date = new Date(System.currentTimeMillis()); //更新当前时间
}
}
public static void sleepTime() throws InterruptedException {
int num = 10;
while (true){
System.out.println(num--);
Thread.sleep(1000);
if (num<=0){
break;
}
}
}
}
线程礼让
-
礼让线程,让当前正在执行的线程暂停,但不阻塞
-
将线程从运行状态转为就绪状态
-
让cpu重新调度,礼让不一定成功
package com.thread.state;
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程正在执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程结束执行");
}
}
礼让成功:
礼让失败:
Join
-
Join 合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
-
可以想象成插队
package com.thread.state;
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("VIP线程来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
for (int i = 0; i < 500; i++) {
if (i == 200){
thread.join();
}
System.out.println("main线程" + i);
}
}
}
从执行结果可以看出,当main 线程执行到200后,VIP线程开始执行,相当于插队执行
线程状态观测
-
Thread.State
-
NEW //尚未启动的线程处于此状态
-
RUNNABLE //在Java虚拟机中执行的线程处于此状态
-
BLOCKED //被阻塞等待监视器锁定的线程处于此状态
-
WAITING //正在等待另一个线程执行特定动作的线程处于此状态
-
TIMED_WAITING //正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
-
TERMINATED //已退出的线程处于此状态
-
package com.thread.state;
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("xxxxxxx");
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state); //NEW
//观察启动后的状态
thread.start();
state = thread.getState();
System.out.println(state); //RUN
while(state != thread.getState().TERMINATED){ //只要线程不终止,就输出状态
Thread.sleep(100);
state = thread.getState();
System.out.println(state); //更新线程状态
}
}
}
首先new一个线程时,它的状态是NEW。然后,启动线程,它的状态是RUNNABLE,但是线程体中有5000毫秒 的 休眠,所以它的状态在这5000 毫秒内是TIMED_WAITING,当5000毫秒结束后,输出了一句话,然后线程结束,然后它的状态变成了TERMINATED,然后跳出while循环
需要注意的是,在while循环中,一定要更新状态,否则它一直是 RUNNABLE