多线程的五种状态
在一个程序运行的过程中,每个线程都存在如下图所示的几种状态
-
新生状态:
在执行new Thread(s)
,线程对象一旦创建就进入新生状态 -
就绪状态:
在线程对象创建完成后调用start
方法,但是线程不会立刻调度执行,而是进入就绪状态,因为在运行前还有一些准备工作要做 -
运行状态:
准备工作完成后进入运行状态,一个运行状态的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供运行的时间(Java的规范说明没有将它作为一个单独状态,准确的说这个状态应该是可运行状态,因为任何时刻,这个状态的某个线程可能正在运行也可能没有运行) -
阻塞状态: 当一个线程被插队、礼让别的线程或者休眠时就会进入到阻塞状态,阻塞状态解除后回到就绪状态,等待CPU调度执行
-
死亡状态 当一个运行状态的线程自然执行完毕后或者中断后线程进入死亡状态,死亡后无法再次启动
多线程方法
void join()
等待该线程终止 相当于插队
插队,要在线程就绪后才能插队,插队会抛出异常
代码示例:
package lainxi;
//线程强制执行
public class TestJoin implements Runnable{
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start(); //开启线程,开启后该线程和主线程同时运行
for (int i = 0; i < 100; i++) {
if (i==80){
//强制执行
thread.join(); //thread线程对象插队
}
System.out.println("我是主线程:"+i);
}
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("我是要插队的线程:"+i);
}
}
}
在主线程的for循环中i为80时,另一个线程开始插队,等另外一个线程执行完成后,才会继续执行主线程
运行结果:
我是要插队的线程:28 //在thread线程对象插队之前,两个线程互相交替执行
我是主线程:34
我是要插队的线程:29
我是主线程:35
我是要插队的线程:30
我是主线程:36
我是要插队的线程:31
我是主线程:37
......
我是主线程:79 //在插队后,必须要让thread线程执行完成后才会回到主线程
我是要插队的线程:65
我是要插队的线程:66
我是要插队的线程:67
我是要插队的线程:68
我是要插队的线程:69
我是要插队的线程:70
我是要插队的线程:71
我是要插队的线程:72
......
我是要插队的线程:98
我是要插队的线程:99
我是主线程:80 //继续执行主线程
我是主线程:81
static void sleep(long millis)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 在之前的代码示例中经常使用该方法来模拟网络延时,而且通过休眠可以扩大问题的发生性
sleep
存在异常InterruptedException
;需要抛出或者捕获
代码示例:
在之前的博客多线程(线程概念、代码示例)中的模拟抢票代码:
package lainxi;
//sleep,模拟休眠 , 扩大问题的发生性.
public class TestSleep1 implements Runnable {
//票
private int ticketNums = 10;
@Override
public void run() {
test();
}
public void test(){
//执行体
while (true){
if (ticketNums<=0){
break;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取线程名字
System.out.println(Thread.currentThread().getName()+"-->抢到了第"+ticketNums--+"张票");
}
}
public static void main(String[] args) {
TestSleep1 t = new TestSleep1();
//第二个参数,创建线程名字
new Thread(t,"小明").start();
new Thread(t,"老师").start();
new Thread(t,"黄牛党").start();
}
}
使用sleep
方法来模拟真实抢票时候的网络延时
代码示例2:
倒计时方法:
private void tenDown() throws InterruptedException {
int num = 10;
for (int i = 10; i > 0; i--) {
Thread.sleep(1000);
System.out.println("倒计时:"+i);
}
}
将该方法定义到主方法外,在主方法调用该方法时,就会让主线程休眠1
秒后打印时间来起到倒计时的作用
package com.kuang.state;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class TestSleep2 {
public static void main(String[] args) throws InterruptedException {
TestSleep2 testSleep2 = new TestSleep2();
//获取系统时间
Date startTime = new Date(System.currentTimeMillis());
while (true) {
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
Thread.sleep(1000);
startTime = new Date(System.currentTimeMillis());
}
}
}
运行结果:
13:19:30
13:19:31
13:19:32
13:19:33
13:19:34
13:19:35
13:19:36
13:19:37
13:19:38
因为在主方法中我定义的是死循环,所以会不断获取时间,但是由于主线程会休眠1秒,所以每隔一秒会打印当前时间
- 停止线程:
查看Thread类的成员方法:
可以看到停止线程的stop
和destroy
方法已经废弃,所以不推荐使用这两种方法来停止线程,可以设置标志位来让线程自然结束,而起到停止线程的作用
static void yield()
暂停当前正在执行的线程对象,并执行其他线程。 也就是礼让,
Thread.State getState()
返回该线程的状态。 返回的是枚举类型
在下面的代码中也穿插了线程状态的查看
package lainxi;
//线程的礼让
public class TestYield {
public static void main(String[] args) throws InterruptedException {
MyYield myYield = new MyYield();
new Thread(myYield,"小明").start();
new Thread(myYield,"老师").start();
Thread thread = new Thread(()-> System.out.println("你好"));
Thread.State state = thread.getState();
System.out.println(state);
thread.start();
state = thread.getState();
System.out.println(state);
state = thread.getState();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (state!=Thread.State.TERMINATED){
Thread.sleep(10);
System.out.println(state);
state = thread.getState();
}
System.out.println(state);
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->启动了");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"-->停止了");
}
}
运行结果为:
小明-->启动了
老师-->启动了
小明-->停止了
老师-->停止了
NEW
RUNNABLE
你好
RUNNABLE
TERMINATED
可以看到在名为小明
的线程执行完第一句代码后开始礼让,执行了老师
线程的第一句,这就是礼让,之后两个线程交替停止
之后还获得了thread
线程的状态并且打印,至于为什么
RUNNABLE
你好
你好
后于RUNNABLE
执行,是因为在线程启动后不会立刻执行,需要一些准备时间,所以会跳过thread.start();
而执行主线程后面的代码,在thread
线程准备完成后,主线程和thread
线程交替执行,在thread完全执行完成后就会死亡,打印TERMINATED
,while
循环终止,程序终止