线程的状态
NEW:尚未启动的线程处于该状态,就是线程new出来了但是没有start。
RUNNABLE:就是线程执行了start后的状态。
BLOCKED:线程由于一些情况被阻塞了,就处于这个状态。
WAITING:线程等待另一个线程执行特定动作就处于此状态。
TIMED_WAITING:线程等待另一个线程执行动作达到指定等待时间就处于此状态。
TERMINATED:已退出的线程处于此状态。
用操作系统中的线程理解就是:
新生状态:new之后的状态。
就绪状态:start之后的状态。
运行状态:占CPU时的状态。
阻塞状态:原本在占CPU由于一些原因被暂停的状态。
死亡状态:线程运行完了。一旦进入死亡状态线程就不能再次启动了。
线程停止
不推荐使用JDK提供的stop()、destroy()方法。【已废弃】
推荐线程自己停下来。
“建议线程自己停下来” 指的是让线程在执行过程中,根据特定的条件判断主动结束自身的执行,而不是通过外部强制的手段(如已废弃的
stop()
、destroy()
方法)来终止线程。通常可以使用一个标志位来控制线程是否继续执行,当标志位满足特定条件时,线程主动退出其执行逻辑。
建议使用一个标志位进行终止,变量flag=false时,终止线程。
示例代码
package com.demo01;
public class ThreadStop implements Runnable {
private boolean flag = true;
@Override
public void run() {
int i = 1;
while (flag) {
System.out.println("新线程执行次数:"+i++);
}
}
public void stop() {
flag = false;
}
public static void main(String[] args) {
ThreadStop threadStop = new ThreadStop();
new Thread(threadStop).start();
for (int i = 1; i <= 100; i++) {
System.out.println("主线程执行次数:"+i);
// 当主线程执行900次时停止新线程
if(i==90){
threadStop.stop();
System.out.println("新线程停止了!!!");
}
}
}
}
ThreadStop threadStop = new ThreadStop();
:创建一个ThreadStop
类的实例threadStop
。new Thread(threadStop).start();
:创建一个新的线程,并将threadStop
作为该线程的任务,然后启动这个新线程。for (int i = 1; i <= 100; i++)
:这是一个for
循环,用于控制主线程的执行次数,从 1 到 100。System.out.println("主线程执行次数:" + i);
:在每次循环中,打印主线程的执行次数。if (i == 90)
:当主线程执行到第 90 次时,执行下面的代码。threadStop.stop();
:调用threadStop
对象的stop()
方法,将flag
变量设置为false
,从而停止新线程的执行。System.out.println("新线程停止了!!!");
:打印一条消息,提示新线程已经停止。
总结
这段代码通过一个标志位 flag
实现了线程的安全停止,避免了使用已废弃的 stop()
方法带来的潜在问题。主线程和新线程可以同时执行各自的任务,并且主线程可以在合适的时机停止新线程的执行。
线程休眠
- sleep指定当前线程阻塞的毫秒数
- sleep存在异常InterruptedException
- sleep时间达到后线程进入就绪状态
- sleep可以模拟网络延时,倒计时等
- 每一个对象都有一个锁,sleep不会释放锁
”每一个对象都有一个锁,sleep不会释放锁“这句话先记住,到后面线程同步时会讲解。
示例代码
模拟一个计时器,计时十秒。
package com.demo01;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ThreadSleep {
public static void main(String[] args) {
Date startTime = new Date(System.currentTimeMillis());
boolean flag = true;
int i = 0;
while(flag) {
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());
if(i++==9){
flag = false;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
while(flag)
:只要flag
为true
,循环就会一直执行。Thread.sleep(1000);
:调用Thread
类的sleep
方法,使当前线程暂停执行 1000 毫秒(即 1 秒)。sleep
方法可能会抛出InterruptedException
异常,因此需要进行异常处理。System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(startTime));
:创建一个SimpleDateFormat
对象,指定日期格式为yyyy-MM-dd HH:mm:ss
,然后使用该对象将startTime
格式化为字符串并打印输出。startTime = new Date(System.currentTimeMillis());
:更新startTime
为当前系统时间,以便下次循环打印新的时间。if(i++==9)
:判断i
的值是否等于 9,如果等于 9,则将flag
设置为false
,从而终止while
循环。i++
表示先使用i
的当前值进行比较,然后再将i
的值加 1。catch (InterruptedException e)
:捕获Thread.sleep
方法可能抛出的InterruptedException
异常。如果捕获到该异常,将其封装在RuntimeException
中并抛出。
总结
这段代码的主要功能是每隔 1 秒打印一次当前时间,总共打印 10 次。通过 Thread.sleep
方法实现了线程的暂停,使用 SimpleDateFormat
类对日期进行格式化输出。
线程礼让
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态
- 让CPU重新调度,礼让不一定成功,看CPU心情。例如我有a、b两个线程并发执行,a中有礼让语句,CPU先调度a线程运行,a礼让,回到就绪状态,CPU重新调度,有可能仍然调度a线程运行,所以不一定成功。
示例代码
package com.demo01;
public class ThreadYield {
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()+"线程结束");
}
}
这里就是创建了两个新线程a,b,然后CPU调度任意一条线程时都会在打印“线程开始”后礼让,然后可能会开启另一条线程,也可能即使礼让了也还是该线程先结束才开始另一条线程。
总结
这段代码的主要功能是创建两个新线程(分别命名为 "a"
和 "b"
),这两个线程会执行 myYield
类中 run()
方法定义的任务。在执行过程中,每个线程会先打印开始信息,然后尝试进行线程礼让,最后打印结束信息。由于线程调度是由操作系统和 JVM 控制的,所以每次运行代码时,两个线程的执行顺序和礼让的效果可能会有所不同。
线程强制执行_join
- Join合并线程,待次线程执行完成后,再执行其他线程,执行join线程时其他线程阻塞。
- 可以理解为在食堂买饭,Join就是插队,插队的时候后面的人都买不了饭(即阻塞)。
示例代码
package com.demo01;
public class ThreadJoin implements Runnable {
@Override
public void run() {
for(int i =1;i<=50;i++) {
System.out.println("线程vip来了"+i);
}
}
public static void main(String[] args) {
ThreadJoin threadJoin = new ThreadJoin();
Thread thread = new Thread(threadJoin);
thread.start();
for (int i = 1; i <= 30; i++) {
System.out.println("这里是主线程"+i);
if(i==20){
try {
thread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
总结
这段代码的主要功能是创建一个新线程 thread
,该线程会循环打印 50 条信息。同时,主线程也会循环打印 30 条信息。当主线程的循环计数达到 20 时,主线程会调用 thread
线程的 join()
方法,暂停自己的执行,等待 thread
线程执行完毕后再继续执行剩余的循环(就是thread线程强占了CPU,要先把这个线程执行完,才能执行其他线程)。这样可以确保在某些情况下,主线程要等待其他线程完成特定任务后再继续执行后续操作。
观测线程状态
使用线程的getState方法可以得到线程此时的状态。
示例代码
package com.demo01;
public class ThreadState {
public static void main(String[] args) {
// new之后的状态
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
System.out.println( thread.getState().toString());
// start之后的状态
thread.start();
System.out.println( thread.getState().toString());
// 写个while循环来观察新线程开始运行到结束的所有状态
while (thread.getState() != Thread.State.TERMINATED) {
try {
Thread.sleep(200);
System.out.println(thread.getState().toString());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 线程结束后的状态
System.out.println( thread.getState().toString());
}
}
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});这里使用Lambda表达式重写了run方法,因为Thread类其实也是实现了Runnable接口的,所以可以直接这样重写run方法,**()是run方法的参数列表,{}**内的内容是方法体。
运行结果:其中出现TIMED_WAITING状态是因为run重写方法中Thread.sleep(500)了,所以有TIMED_WAITING
NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TERMINATED
总结
这段代码通过创建一个新线程,启动线程并使用 while
循环持续监测线程状态,展示了线程从创建到终止过程中状态的变化。Thread.sleep
方法用于控制线程的执行时间,模拟耗时操作,同时也展示了线程在休眠时的状态。