前言:
小亭子正在努力的学习编程,接下来将开启javaEE的学习~~
分享的文章都是学习的笔记和感悟,如有不妥之处希望大佬们批评指正~~
同时如果本文对你有帮助的话,烦请点赞关注支持一波, 感激不尽~~
目录
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 的几个常见属性
属性 | 获取方法 | 作用 |
ID | getId() | 表示线程的唯一身份标识,不会重复 |
名称 | 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) --> 系统内核中真下在的线程PCB3.线程分为后台线程和前台线程后台线程也叫守护线程。每⼀个进程都有⼀个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
线程的状态
线程有如下几种状态
- NEW : 表示已经实例化了 Thread 对象, 但是还没有调用 start 方法启动线程
- RUNNABLE : 表示就绪状态, 正在执行或者准备随时执行
- TERMINATED : 线程执行完毕
- TIMED_WAITING : 表示指定时间等待, 比如由于 sleep 方法, 线程休眠时的状态
- BLOCKED : 表示等待锁出现的状态
- WAITING : 表示由于 wait 方法出现的状态
观察一下前四个状态, 后两个状态后面再详细介绍
执行睡眠时的状态
状态转换
状态转换de主要过程很简单,说白了就是一条主线, 三条支线
一条主线:
- 线程启动之前 : NEW 状态
- 线程执行时 : RUNNABLE 状态
- 线程结束之后 : TERMINATED 状态
三条支线
- 第一条支线 : 如果使用 sleep方法, 就进入TIMED_WAITING 状态
- 第二条支线 : 如果进行加锁操作, 就进入BLOCKED 状态
- 第三条支线 : 如果使用 wait方法, 就进入WAITING 状态
现在咱们只需要大致理解线程状态, 好处就是在后续进行多线程代码调试时, 能够根据当前线程的状态进行初步分析。
start()和run()⽅法的区别
调⽤start()⽅法后JVM去调⽤系统API, 这时才真正去的申请系统线程, 申请到了系统线程PCB后, 才准备参与CPU调度, 这个是真正的创建线程的⽅法
调⽤run()⽅法, 只是⼀个普通针对对象的⽅法调⽤⽽已, 和调⽤⼀个对象的toString()⽅法没有啥区别。
以上就是本文的所有内容~~~~
如果本文对您有帮助,记得一键三连哦~~~~