Java多线程——与线程状态的相关操作(线程停止、休眠、礼让、强制执行、观测状态)

线程的状态

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):只要 flagtrue,循环就会一直执行。
  • 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 方法用于控制线程的执行时间,模拟耗时操作,同时也展示了线程在休眠时的状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值