0
点赞
收藏
分享

微信扫一扫

Qt中的事件学习笔记

谷中百合517 04-09 15:00 阅读 1
java-eejava

在这里插入图片描述

T04BF

👋专栏: 算法|JAVA|MySQL|C语言

🫵 小比特 大梦想

目录

3.Thread类及常见方法

3.1Thread常见的构造方法

在这里插入图片描述
(1)String name,就是在创建线程的时候,给线程起个名字,实际上是否有名字对线程本身没有任何影响,即使你不起名字,线程也会有默认的名字:Thread-0,Thread-1…,只不过起了名字后,当进程执行的过程中,可以通过jconsole / idea看到不同线程的名字,出现问题的时候,更加直观的把问题线程和代码关联起来,方便调试

public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(true){
                System.out.println("hello t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"自定义线程");
        t.start();
        while(true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

在这里插入图片描述
(2)对于ThreadGroup,在开发中很少见到,有的时候,希望把多个线程进程分组,分组之后,就可以针对不同的分组进行批量控制
但是这种写法目前在实际开发中更多的是被线程池取代了

3.2Thread常见的几个属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

(1)id 这里的id和pcb里面的pid是不一样的,这里的id是jvm自己搞的一套id体系,而我们通过java代码是不能获取到pcb里面的id的

(2)优先级
实际上虽然iava而提供了修改优先级的接口,但是即使你修改了优先级,现象也是不明显的,因为你修改了优先级是一回事,系统调度又是一回事,这里你修改了优先级只能说是一种"建议",具体还是以系统为主

而不仅是在java,即使你通过c去调用系统原生的修改优先级的api,效果也是不明显的,本质上还是因为调度这个事情,操作系统就一堂言了,程序员很难干预到

(3)前台线程和后台线程
对于前台线程,这样的线程不结束的话,java进程是一定不会结束的
对于后台线程,这样的线程即使继续执行,也是无法阻止java进程结束的

前台线程是可以有多个的,但是是有是当最后一个前台线程结束后,进程才算真正结束

在java中,main就是前台线程,而我们自己创建的线程默认也是前台线程,至于其他jvm创建的线程就是后台线程了,比如GC,GC是要周期性持续性的运行的,如果这样的线程设置为前台线程,那么进程就再也结束不了了

在这里插入图片描述
如图所示,main线程和t线程都是前台线程的时候,即使main结束了,进程也没有结束,必须等到t线程结束,进程才结束

在java中,我们可以通过setDaemon来将线程设置为后台线程
在这里插入图片描述
此时由于只有main线程是前台线程,那么一旦前台线程结束后,进程也就结束了

(5)是否存活
指的是在系统中的pcb(线程)是否存活
值得注意的是,线程的生命周期和java中Thread对象的生命周期不一定完全一样的
我们来对比一下下面的两段代码

public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {

        });
        t.start();
        Thread.sleep(1000);
    }
}

此时由于T线程里面没有做什么事情,因此操作系统内核里面对应的pcb一下子就销毁了,但是由于主线程调用了sleep,在sleep结束之前,Thread对象是不会销毁的

public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t.start();
        t = null;
    }
}

此时在t线程结束之前,t线程指向的对象就要被GC回收了

因此,对于线程的生命周期和线程对应对象的是生命周期是不一样的,不能说谁长谁短,那么我们就可以利用isAlive来判断系统中的线程是否还存在

4.中断一个线程

中断线程,在java中也只是"提醒、建议"真正要不要终止,还是要线程本身来决定的
即假设t线程正在执行,其他线程只能是提醒一下t线程是否要终止,t收到这样的提醒后,也是靠自己决定要不要终止

4.1自己实现控制线程结束

private static boolean isRunning = true;
public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(() -> {
       while(isRunning){
           System.out.println("t线程运行");
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               throw new RuntimeException(e);
           }
       }
        System.out.println("t线程结束了");
    });
    

    t.start();
    //3s后,让t线程结束
    Thread.sleep(3000);
    System.out.println("控制t线程结束");
    isRunning = false;
}

但是假设这里t线程的sleep时间是10s,此时main线程是无法让t线程即使终止掉的

4.2调用Thread提供的interrupt方法

我们刚刚是自己定义的一个boolean变量,实际上Thread里面内置了一个,使用内置的标志位,功能更加强大
在这里插入图片描述
interrupt(): 这是一个实例方法,用于中断线程。当调用某个线程的 interrupt() 方法时,会给该线程设置一个中断标志,表示该线程已被请求中断。如果线程处于阻塞状态(如等待、睡眠或在输入输出操作中被阻塞),它会收到一个 InterruptedException 异常,以此来响应中断请求。在其他情况下,需要检查线程的中断状态,并采取适当的行动来响应中断请求。

interrupted(): 这是一个静态方法,用于检查当前线程是否被中断,并且会清除线程的中断状态。如果调用线程的 interrupted() 方法返回 true,则表示该线程之前被中断过,并且清除了中断状态;如果返回 false,则表示该线程之前未被中断过。通常,这个方法用于检查当前线程是否被中断,并在必要时做出相应的处理。

isInterrupted(): 这是一个实例方法,用于检查线程是否被中断,但不清除中断状态。调用线程的 isInterrupted() 方法返回 true 表示该线程被中断,返回 false 表示未被中断。通常,这个方法用于检查其他线程的中断状态,而不影响当前线程的中断状态。
在这里插入图片描述
当一个线程正在sleep的时候被中断,此时就会抛出InterruptedException异常,即立即唤醒线程,不会再等待了

但是如果我们不采用抛出异常的方法:
在这里插入图片描述
怎么t线程还会继续执行???
实际上是由于sleep,如果调用interrupt方法后,线程没有sleep,确实是直接修改标志位就完了
但是如果正在sleep,那么就会立即唤醒sleep,但sleep在被唤醒的同时,也会清除刚刚的标志位!!!
之所以要清除标志位,实际上也就是要将控制权交给程序员自己,当前代码是要继续执行?还是直接结束,程序员就可以通过代码来决定了
在这里插入图片描述

5 .等待一个线程

在操作系统中.多个线程之间的调度顺序是随机的(抢占式执行),但是我们往往希望线程的执行顺序是稳定的,因此我们希望在随机的体系上,让结果变得不那么随机
具体来说,一个线程数什么时候调度是不确定的,多个线程谁先开始谁先结束是不确定的
我们通过线程的等待,就是要能够确线程结束的先后顺序
在这里插入图片描述
如果不加join方法,那么main线程和t线程谁先结束是不确定的
但是在main线程里面调用t.join,就是要让main线程去等待t线程,也就是t线程结束后,main线程才继续执行下面的逻辑
此时就确保了main线程一定是后结束的那一个线程

实际上join也提供了带超时的版本
在这里插入图片描述
只带一个参数的join,代表等待N ms

带两个参数则精确到毫秒和纳秒

(但是实际上纳秒这个级别的时间,对于主流系统来说,都是太精细了.像windows / linus这样的系统,无法精确到ns级别的时间 甚至到了ms级别都有可能出差错.而且达到ns这种级别的系统开销太大了)

传入的时间则代表最长的等待时间,比如写了10ms,如果10ms还没到,t线程已经结束了,那么就直接返回

如果10ms到了,t线程还没结束,那么就不等了,继续往下走

这种场景应用还是非常广泛的

假如A中的主线程创建t线程,t线程给B发送请求,紧接着A就让主线程t.join等待t线程结束(获取到B响应)

如果B挂了.此时如果是使用死等的策略,就会使A中的主线程卡住了,无法再继续执行后面逻辑了

这种情况就需要制定好超时时间,超过时间就不等了,继续做该做的事情
在这里插入图片描述

6.获取当前线程的引用

public static Thread currentThread();

返回的是当前线程对象的引用

7.线程的状态

我们在上一章节谈到的进程的状态实际上是线程的状态或者叫做pcb的状态
在Java中,对线程的状态大体是分成6中不同的状态

7.1 new

表示Thread对象已经有了,但是还没有start,即操作系统内核的pcb还没创建

7.2 terminated

表示线程已经终止了,即内核中的线程已经销毁了,但是Thread对象还在

7.3 Runnable

分成两种
(1)指的是当前线程正在cpu上执行
(2)指的是这个线程虽然没有在cpu上执行,但随时可以调度到cpu上执行

7.4阻塞

(1)waiting 死等进入的阻塞
(2)timed_waiting 带有超时版本的阻塞(sleep属于其中的一种)
(2)blocked 进行锁竞争时候产生的阻塞(后面会谈到)

7.5查看当前线程的状态

(1)getState
在这里插入图片描述
(2)jconsole
在这里插入图片描述

感谢您的访问!!期待您的关注!!!

在这里插入图片描述

T04BF

🫵 小比特 大梦想
举报

相关推荐

0 条评论