多线程01(线程状态和线程的sleep,线程终止(Interrupt)的小关联)

目录

一、前面总结

[二、sleep() 函数修改 interrupt() 改变线程的中断状态,让 interrupted 成员变量被 interrupt() 修改成 true 之后给改回原始状态 false(没有被终止的状态)。](#二、sleep() 函数修改 interrupt() 改变线程的中断状态,让 interrupted 成员变量被 interrupt() 修改成 true 之后给改回原始状态 false(没有被终止的状态)。)

[三. 等待一个线程------join()](#三. 等待一个线程——join())

[3.1 Thread.sleep() 休眠当前线程](#3.1 Thread.sleep() 休眠当前线程)

[四、 线程状态](#四、 线程状态)

五、调试小技巧

[六、 多线程带来的风险](#六、 多线程带来的风险)

七、在cpu指令角度看

[count++; 虽然是一行代码,但是这个操作对应到cpu上 3个cpu指令:](#count++; 虽然是一行代码,但是这个操作对应到cpu上 3个cpu指令:)


一、前面总结

    1. 线程创建
    1. 创建子类去继承 Thread重写run
    1. 创建子类实现 Runnable重写run
    1. 基于**(1)**使用匿名内部类
    1. 基于的**(2)**使用匿名内部类
    1. 基于lambda表达式

    1. Thread 核心属性
  • name 线程在创建的时候可以起名字
  • isDaemon / setDaemon 可以修改线程是否是后台线程isAlive 检查线程是否存活存活;因为Thread 对象和内核中的线程是一一对应的,可能出现内核中的线程已经结束销毁,但Thread对象还在

  1. 3. Thread的start方法
  • Thread对象只能start一次

  1. 4.线程的终止
  • 核心让线程的入口方法,能自尽快地结束
  • i**nterrupt()**让线程结束
  • isInterrupted() 判断该线程是否被终止了,里面会有一个成员变量来记录状态
  • 就像下main 这张图片中,在调用 interrupt 函数之后会把 interrupted 这个变量修改成 true ,这样在 isInterrupted 函数就会返回 true 表示当前线程被终止了。

二、sleep() 函数修改 interrupt() 改变线程的中断状态,让 interrupted 成员变量被 interrupt() 修改成 true 之后给改回原始状态 false(没有被终止的状态)。

java 复制代码
public class Thread01 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while(!Thread.currentThread().isInterrupted())
                try{
                    Thread.sleep(1000);
                } catch (InterruptedException e){
                    break;
                }
        });
        t.start();
        Thread.sleep(1000);
        t.interrupt();
    }
}

这样操作时,整个 while 循环中,大部分时间都是在 sleep 中此时 main 线程调用 interrupt() ,这时候 t 线程大概率卡在sleep此时sleep()被强制唤醒会抛出 InterruptedExeception 异常,程序捕获 InterruptedExeception 异常之后进入 catch 语句,里面有 break 语句跳出循环

如果没有break语句,是空语句呢?

java 复制代码
public class Thread01 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while(!Thread.currentThread().isInterrupted())
                try{
                    Thread.sleep(1000);
                } catch (InterruptedException e){
//////////////////   什么都不写
                }
        });
        t.start();
        Thread.sleep(1000);
        t.interrupt();
    }
}
  • 正常说 :是不是在interrupt 会修改 t 线程中的值,让它终止,此时t.isInterrupted() 的值为true 就是终止的线程 ;但是此时 t 线程在 sleep 中,唤醒 sleep ,然后执行它的执行语句,结束当前循环,进入 while 循环的判断条件中isInterrupt() 函数返回 true 取反之后结束循环。
  • 但是现象却是又继续在动是为什么?
    因为 interruptsleep 唤醒了,在这种提前唤醒 的情况下,sleep 函数会把isInterrupted 标志位给设置回false,这时候又编程没有终止线程的状态了。但是会进入 catch 语句中,如果我们不去做操作,他就会认为该程序不应该结束,这里你写入 continue 也是一样会一直循环,不会结束循环。
java 复制代码
public class Thread01 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while(!Thread.currentThread().isInterrupted())
                try{
                    Thread.sleep(1000);
                } catch (InterruptedException e){
                    // break;
                    continue;
                }
        });
        t.start();
        Thread.sleep(1000);
        t.interrupt();
    }
}

可以理解为:

sleep这样设定之后,相当于让线程在catch语句中有更多的选择空间,线程又可以自行决定,咱们这线程要立即终止,还是等会结束,还是不结束(忽略这个终止信号)


三. 等待一个线程------join()

多个线程并发执行随机调度 站在程序员的角度是不好 的,咱们不喜欢随机,不可控的东西。但是join() 函数能够要求,多个线程之间,给指定的顺序 例如在 main()中,... t.join(); 这就是主线程等t线程结束再执行面的语句,此时主线程进入阻塞等待的状态

  • t.join(3000) 如果3000ms 内,t 线程结束了也好,就执行下面的语句;如果超过 3000ms还没结束,此时主线程就不等了,直接往下执行
  • t.join(1000, 500),纳秒级
  • public static Thread currentThread(); 返回Thread的引用哪个线程调用,返回哪个线程的调用(是哪个线程)

3.1 Thread.sleep() 休眠当前线程

  • 因为线程的调度是不可控的所以这个方法只能保证实际休眠时间大于等于参数设置的休眠时间。
  • 因为使用 sleep ,相当于让当前线程让出cpu的资源 后续时间到了的时候,需要操作系统内核把这个线程重新调到cpu上,才能继续执行,所以此时会导致大于参数时间
  • sleep() 使用sleep意味着当前的线程,立即放弃CPU资源,等待操作系统重新调度,可以使得当前线程不影响后续线程,可以防止当前线程占用 CPU 时间过长影响后续线程发生饿死。

四、 线程状态

  • NEW : 安排了工作,但未启动,New了Thread对象,还没start
  • TERMINATED : 工作完成了,终止, 内核中的线程已经结束,但Thread对象还在
  • RUNNABLE : 可运行的,又分为正在工作中和等待运行的工作
    就绪:
    1) 线程正在cpu上执行
    2). 线程随时可以去cpu上执行
  • TIMED_WAITING指定时间的阻塞,线程阻塞 (不竞争CPU的调度,不能被执行),阻塞的时间是有上限的
  • WAITING死等,没有超时时间的等待
  • BLOCKED:也是一种阻塞,比较特殊,由于锁导致的阻塞

大概状态图可以看下面这个表

五、调试小技巧

比如,发现代码中某个逻辑好像卡死了(明明调用了,没有执行/没有执行完)

  1. jconsole (监控)查看当前的进程中的所有线程,找到对应逻辑的线程进程

2. 看各线程状态是啥?

看到TIMED_WAITING/WAITING怀疑这里是代码中某个地方产生的阻塞,没有被及时唤醒

看到BLOCKED怀疑是不是代码中出现的死锁

看到RUNNABLE,线程本身没问题,考虑逻辑上某些事件有没有被其它线程触发的
只有在具体的调用栈中找(尤其是阻塞的状态,线程代码的阻塞在哪一行了)

六、 多线程带来的风险

java 复制代码
public class Thread02 {
    private static int count = 0;
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for(int i = 0; i < 50000; i++) { count++; }
        });
        Thread t2 = new Thread(() -> {
            for(int i = 0; i < 50000; i++) { count++; }
        });
        t1.start(); t2.start();
        System.out.println(count);
    }
}

这里 t1 线程,t2 线程都会给 count 加了5万次应该为10万但是结果却不是10万次 ,因为在两个线程同时操作时,t1和t2线程都拿了count都++了 ,所以此时的++的数量减少两位,但是值却只变了1。
count++两次,值改变1
解决方法

先让一个执行完 ,再执行下一个。t1.start();t1.join(); t1线程从开始到结束中间没有穿插其他线程,保持当前只执行一个 t1 线程s2.start(); s2.join(); 一个执行完再执行下一个 ,这时候这两个线程就变成了串联执行了t1 执行完成5000 次**++** 操作之后,t2 线程才会开始执行这时候t2再去执行它的++操作50000次这样两个线程就不涉及到同时修改一个变量的情况了。

java 复制代码
public class Thread02 {
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for(int i = 0; i < 50000; i++) { count++; }
        });
        Thread t2 = new Thread(() -> {
            for(int i = 0; i < 50000; i++) { count++; }
        });
        t1.start(); 
        t1.join();
        t2.start();
        t2.join();
        System.out.println(count);
    }
}

七、在cpu指令角度看

count++; 虽然是一行代码,但是这个操作对应到cpu上 3个cpu指令:

  1. load,把内存中的值(count变量)读取到cpu寄存器中
  2. add,把指定寄存器中的值进行+1操作(结果仍保存在寄存器中)
  3. save,把寄存器中的值,写回内存中由于操作系统调度是随机的,执行任何一个指令的过程中,都可能发生上述的线程的切换操作。
相关推荐
山甫aa7 小时前
Java的包和import
java·开发语言
星轨zb8 小时前
JUC 到 Redis 分布式锁:一次关于高并发的性能压测实验
java·redis·分布式·jmeter
深蓝轨迹8 小时前
Java 集合框架超全解 · 底层源码|集合对比|HashMap 扩容原理
java·hashmap·集合框架·arraylist·linkedlist
hansang_IR8 小时前
【记录】loj2967「COCI 2010.03.06」PROGRAM
c++·算法
心中有国也有家8 小时前
PaddlePaddle 适配 NPU 的技术全解析——从算子接入到端到端性能优化
人工智能·分布式·算法·性能优化·架构·paddlepaddle
兰令水8 小时前
topcode【随机算法题】【2026.5.24打卡-java版本】
java·开发语言·算法
LCG元8 小时前
Istio - 服务网格流量治理深度解析:灰度发布 / 故障注入配置实践
java·数据库·istio
hef2888 小时前
Java Switch和Break语句用法详解:从入门到实战
java·开发语言
ABCDEEE79 小时前
3.RAG
java·linux·服务器