线程状态
1.线程状态转换图
2.线程状态的进一步理解
new:Thread thread = new Thread(),线程对象一旦创建好了,就进入了新生状态;
就绪状态:当调用start()方法,线程立即进入就绪状态,但是这并不意味着该线程会立即被CPU调度执行;
运行状态:线程被CPU调度执行后,才算真正进入运行状态,线程才真正执行线程体的代码块;
阻塞状态:当调用sleep()、wait()方法或同步锁定时,线程进入阻塞状态,就会导致代码不会继续往下执行,只有当阻塞状态解除后,该线程才可以重新进入就绪状态,等待CPU的调度执行;
死亡:线程中断或者结束,一旦进入死亡状态,该线程就不能再次启动了。
3.线程方法
4.停止线程
A.不推荐使用JDK提供的stop()、destroy()方法。当你进入Thread类查看其中的方法时,你会发现这两个方法上画了一条横线,表示该方法已经废弃,不推荐使用;
B.推荐线程自己停下来;
C.建议使用一个标志位进行终止变量,例如:当flag = false,则线程终止。
D.代码如下:
//测试stop
//1.建议使用正常的停止,声明利用次数,尽量不要用死循环;
//2.建议使用标志位,就需要我们自己设置一个标志位;
//3.不要使用stop()或destroy()等过时或者JDK不推荐使用的方法;
public class TestStop implements Runnable {
//1.设置一个标志位
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();
Thread thread = new Thread(testStop);
thread.start();
for (int i = 0; i < 1200; i++) {
System.out.println("我是主线程" + i);
if(i == 350) {
//调用stop()方法,改变标志位的值
testStop.stop();
System.out.println("当循环次数达到" + i + "次,线程被停止了");
}
}
}
}
运行结果:
如果能够得到以上运行结果,就证明线程停止成功。
5.线程休眠
A.sleep(时间)指定当前线程阻塞的毫秒数;
B.sleep存在异常InterruptedExcep;
C.sleep时间达到后,线程进入就绪状态;
D.sleep可以模拟网络延时,倒计时等;
E.每一个对象都有一个锁,sleep不会释放锁。(后期会说明原因)
F.代码:
//模拟延时:放大问题的发生性
public class TestSleep implements Runnable {
private int tickets = 15;
@Override
public void run() {
while(true) {
if(tickets <= 0) {
break;
}
//延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买了" + tickets-- + "号票");
}
}
public static void main(String[] args) {
TestSleep saleFilms = new TestSleep();
new Thread(saleFilms, "张三").start();
new Thread(saleFilms, "李四").start();
new Thread(saleFilms, "王五").start();
}
}
补充:
在上一篇的线程实现中的卖电影的例子中,提出了线程的不安全,当时第二种方法,采用了延时,是为了放大线程不安全问题的发生性。
//模拟倒计时:十秒倒计时器
public class TestSleep2 {
public static void main(String[] args) throws InterruptedException {
countDown();
}
public static void countDown() throws InterruptedException {
int num = 10;//倒计时的时间,长短可以自己定;
while(true) {
//这里会出现异常,我们采用抛出的方式;
Thread.sleep(1000);
System.out.println(num--);
if(num <= 0) {
break;
}
}
}
}
import java.text.SimpleDateFormat;
import java.util.Date;
//模拟计时器:这里是进行的是从当前时间开始,进行计时
public class TestSleep2 {
public static void main(String[] args) {
//获取当前时间
Date startTime = new Date(System.currentTimeMillis());
while(true) {
try {
Thread.sleep(1000);
String s = new SimpleDateFormat("HH:mm:ss").format(startTime);
//打印当前时间
System.out.println(s);
//重新生成下一个时间
startTime = new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
6.线程礼让
A.礼让线程,让当前正在执行的线程暂停,但不会阻塞;
B.将线程从运行状态转为就绪状态;
C.让CPU重新调度,礼让不一定成功!看CPU心情,决定权掌握在CPU手里。
D.代码:
//测试线程礼让:
//核心:礼让不一定成功,由CPU决定(调度算法);
public class TestYield {
public static void main(String[] args) {
doYield doYield = new doYield();
new Thread(doYield, "B").start();
new Thread(doYield, "A").start();
}
}
class doYield implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行");
Thread.yield();//实现线程礼让;
System.out.println(Thread.currentThread().getName() + "线程停止执行");
}
}
运行结果为:
如果得到了以上结果,就说明礼让成功了;但是也会出现礼让不成功的现象(多运行几次,就可以看到),如:
因此,礼让不一定成功,决定权掌握在CPU手里!
7.线程插队(Join)
A.把它理解为生活中的插队现象即可;
B.概念:Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
C.代码:
//测试join()方法:想象成插队即可
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 500; i++) {
System.out.println("我是VIP,我先执行");
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 = 1; i <= 100; i++) {
if(i == 50) {
thread.join();//抛出异常
}
System.out.println("主线程执行了" + i + "次");
}
}
}
观察程序的运行结果,会发现,当主线程运行到50次时,子线程会强行插队执行,并且主线程要等待子线程运行完毕后,才接着继续执行。
8.观测线程状态(Thread.State)
线程可以处于一下状态之一:
A.NEW
尚未启动的线程状态处于此状态。
B.RUNNABLE
在Java虚拟机中执行的线程处于此状态。
C.BLOCKED
被阻塞等待监视器锁定的线程处于此状态。
D.WAITING
正在等待另一个线程执行特定动作的线程处于此状态。
E.TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
F.TERMINATED
已退出的线程处于此状态,也可以理解为死亡的线程处于此状态。
注意:一个线程可以在给定的时间点处于一个状态。这些状态是不反映任何操作系统线程状态的虚拟机状态。
G.代码:
//测试线程的状态
public class TestState implements Runnable {
public static void main(String[] args) {
TestState testState = new TestState();
Thread thread = new Thread(testState);
//观察状态
Thread.State state = thread.getState();
System.out.println(state);
//观察启动后
thread.start();//启动线程
state = thread.getState();
System.out.println(state);
while(state != Thread.State.TERMINATED) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//更新状态
state = thread.getState();
System.out.println(state);
}
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("*******************");
}
}
程序运行结果:
这就是线程的五种状态,我们可以通过利用线程的这几种状态,来完成一些小事情,例如:
a.如果一个线程处于等待状态,就让它注销等;
b.如果一个线程在一定的时间里它都是等待状态,我们就可以让它崩溃,让它停止下来;
c.如果线程状态是TERMINATED,它就再也启动不了了。如果,再在TERMINATED后调用start()方法,会出错,如下图:
因此,死亡之后的线程是不可以再执行的。
本次讲述了线程状态,有关线程的知识点,仍在更新。