【多线程】Thread类及其常用的方法+线程的状态

前言:

小亭子正在努力的学习编程,接下来将开启javaEE的学习~~

分享的文章都是学习的笔记和感悟,如有不妥之处希望大佬们批评指正~~

同时如果本文对你有帮助的话,烦请点赞关注支持一波, 感激不尽~~

目录

Thread 类

Thread 的常见构造方法

Thread 的几个常见属性

​编辑

启动一个线程——start()方法

中断一个线程——interrupt()

方法一:手动给线程设置一个标志位,让线程结束

方法二:Thread类 内置了一个标志位

interrupted ()方法的作用:

等待一个线程——join()

休眠线程—— sleep()

线程的状态

线程有如下几种状态

状态转换

start()和run()⽅法的区别


Thread 类

Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联

Thread 的常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用 Runnable 对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名
【了解】Thread(ThreadGroup group,
Runnable target)
线程可以被用来分组管理,分好的组即为线程组,这
个目前我们了解即可

Thread 的几个常见属性

属性获取方法作用
IDgetId()表示线程的唯一身份标识,不会重复
名称getName()表示线程的名字,没啥实际作用,主要是方便代码调试
状态getState()表示线程所处的情况(下面再详细说明)
优先级getPriority()理论上,优先级高,线程会被优先调用(但这只是理论上,实际情况很复杂)
是否后台线程isDaemon()

是返回true,不是false

默认为前台,前台线程会阻止进程的结束,后台线程不会【说明:java进程中所有前台线程都结束了该进程,但是不管后台线程结没结束,进程该结束就结束】

可以通过  setDaemon()把线程修改为后台线程

是否存活isAlive()是否存活,简单理解为run方法是否运行结束
是否被中断isInterrupted()获取是否被中断使用【下面详细说明】

说明:

1.如果不为线程指定名字,那么JVM会为每个线程⾃动⽣成⼀个名字,如Thread-0, Thread-1....起名字的作⽤是为了让程序员⽅便调度代码,出错的时候可以看出是哪个线程出错的,可以⽤hread.currentThread().getName()获取线程名可以通过获类名+获取⽅法名+获取线程名定位⼀个问题出现在哪个类,哪个⽅法,哪个线程

2.ID - getId()JVM为线程⽣成的⼀个Id编号,注意这个是JVM层⾯的,和系统内核中的PCB的PID不是⼀回事⼤家注意区分调⽤过程 JVM --> 系统调⽤(系统对外提供的API,C、C++语
⾔语⾔写的关于线程 的API) --> 系统内核中真下在的线程PCB

3.线程分为后台线程和前台线程后台线程也叫守护线程。每⼀个进程都有⼀个main线程,如果基本线程是前台进程,即使main线程执⾏完了,进程也不会退出,⽽是要等到所有的前台
线程执⾏完才退出,当其他线程是后台线程时,意味着不会阻⽌进程退出,当main线
程执⾏完最后⼀⾏代码,即使还有后台线程在执⾏,程序依然会退出

 public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            System.out.println("hello t");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        //启动之前.获取线程状态
        System.out.println("----获取启动之前的状态");
        System.out.println(thread.getState());
        System.out.println("查看thread线程是否存活:"+ thread.isAlive());
        //启动线程
        System.out.println("线程运行中");
        thread.start();   //启动线程
        System.out.println("thread线程 是否存活:" + thread.isAlive());
        System.out.println("获取thread线程的 id" + thread.getId());
        System.out.println("获取thread线程的 name" + thread.getName());
        System.out.println("获取thread线程的 状态" + thread.getState());
        System.out.println("获取thread线程的 优先级 " + thread.getPriority());
        System.out.println("获取thread线程 是否为后台程序 " + thread.isDaemon());
        System.out.println("获取thread线程 是否被中断 " + thread.isInterrupted());
        Thread.sleep(3000);
    }


启动一个线程——start()方法

【这个方法前面已经使用过很多次了,这里咱们做一些总结~~】

  • 重写了 run 方法只是用代码描述了线程要执行什么操作
  • 重写的run是系统自动调用的,不是咱们手动调用的,咱们手动调用的run方法不能真正启动线程
  • 上一篇介绍了很多方式用来创建线程对象, 但是创建了线程对象不代表线程开始执行,使用start()方法才能正式启动这个线程,这个线程才会真正独立执行。

【举个栗子:小明在打游戏,他妈妈叫他去打酱油,他说好,但是他没有立刻去,而是游戏结束后立刻去了,这个例子中 去打游戏是新的线程,他妈妈叫他去打酱油是创建了线程,游戏结束是start方法,】


中断一个线程——interrupt()

public void interrupt()中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,
否则设置标志位
public static boolean
interrupted()
判断当前线程的中断标志位是否设置,调用后清除标志位
public boolean
isInterrupted()
判断对象关联的线程的标志位是否设置,调用后不清除标志位

所谓中断, 就是让一个程序停下来
本质上来说,让一个进程停下来的方法就一种,让该线程的入口方法 执行完毕。

方法一:手动给线程设置一个标志位,让线程结束

注意:lambda表达式在的变量获取,捕获的变量必须是final 修饰 或者 没用final修饰,但是代码中并没有做出修改。

方法二:Thread类 内置了一个标志位

isInterrupted()方法就是这个默认的标志位,默认为flase,作用类似于上面的flag

interrupted ()方法的作用:

1.设置标志位为true
2.如果该线程正在阻塞中(如正在执行sleep)此时就会把阻塞状态唤醒,,通过抛出异常的方式让sleep立刻结束。

【注意:当thread线程的sleep第一次被唤醒的时候自动的把isInterrupted的标志位给清空(true->flase),第二次执行的时候就没有这个中断标志位(中断标志变成了flase不会触发中断)

那么肯定有小伙伴们会问 为什么只是抛出异常而不是直接中断运行?
因为这样很不友好,用isInterrupted方法的效果不是让线程立刻结束,而是告诉他该结束了,至于线程是否结束都是由代码来灵活控制的。
那么在上述代码中, 如何实现立即中断呢? 只需要在捕获异常之后加一个 break 就行啦~~


补充一下:每个线程都是一个独立的执行流】


等待一个线程——join()

线程之间是并发执行的,操作系统对于线程的调度是无序的,无法判定两个线程谁先执行,谁后执行。
但是,有的时候, 多个线程不能满足并发的条件, 可能需要等某个线程执行完再并发执行
【举个栗子:例如我要和女朋友去约会, 她让我去去她家楼下接她, 我到她家楼下后, 她还在化妆, 我需要等她化好妆下楼之后去约会。】

方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos)同理,但可以更高精度

回顾一下多线程并发执行的效果(打印顺序的无序的)

运用 thread.join()  执行线程等待操作后的效果

 
可以看到, 当 thread线程 执行完了所有的循环打印之后, 主线程 才开始循环打印, 说明此时, 主线程 是等待 thread线程 结束之后才执行的。

所以 我们可以得出一个结论 :谁调用的 join()方法,谁就等待 thread线程执行
另外, join方法 还可以传入一个参数, 和 sleep方法 的参数一致, 单位是毫秒,


休眠线程—— sleep()

【关于 sleep方法以上代码已经使用过很多次了, 这里再做个总结 】

  • 参数为毫秒, 表示 sleep方法 执行后, 线程休眠多久
  • 需要用 try-catch 捕获 InterruptedException 这个受查(编译时)异常, 如果休眠时被中断, 就会抛出异常
  • 在休眠时如果被唤醒, sleep方法 会自动把标志位清空: 设置成 false
     

线程的状态

线程有如下几种状态

  1. NEW : 表示已经实例化了 Thread 对象, 但是还没有调用 start 方法启动线程
  2. RUNNABLE : 表示就绪状态, 正在执行或者准备随时执行 
  3. TERMINATED : 线程执行完毕
  4. TIMED_WAITING : 表示指定时间等待, 比如由于 sleep 方法, 线程休眠时的状态
  5.  BLOCKED : 表示等待锁出现的状态
  6. WAITING : 表示由于 wait 方法出现的状态

观察一下前四个状态, 后两个状态后面再详细介绍

执行睡眠时的状态


状态转换

状态转换de主要过程很简单,说白了就是一条主线, 三条支线

一条主线:

  • 线程启动之前 : NEW 状态
  • 线程执行时 : RUNNABLE 状态
  • 线程结束之后 : TERMINATED 状态

三条支线

  • 第一条支线 : 如果使用 sleep方法, 就进入TIMED_WAITING 状态
  • 第二条支线 : 如果进行加锁操作, 就进入BLOCKED 状态
  • 第三条支线 : 如果使用 wait方法, 就进入WAITING 状态

现在咱们只需要大致理解线程状态, 好处就是在后续进行多线程代码调试时, 能够根据当前线程的状态进行初步分析。


start()和run()⽅法的区别

调⽤start()⽅法后JVM去调⽤系统API, 这时才真正去的申请系统线程, 申请到了系统线程PCB后, 才准备参与CPU调度, 这个是真正的创建线程的⽅法
调⽤run()⽅法, 只是⼀个普通针对对象的⽅法调⽤⽽已, 和调⽤⼀个对象的toString()⽅法没有啥区别。


以上就是本文的所有内容~~~~

如果本文对您有帮助,记得一键三连哦~~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值