一、前言
- 在前一篇文章《Java_多线程 — 线程的 5 大状态》中,我们详细地分析了线程的五大状态,相信大家有所收获!希望大家可以好好阅读前一篇以顺利过渡到该篇
二、大纲
sleep()
yield()
join()
线程优先级
高效结束线程
三、详述
-
3.1、
线程睡眠——sleep()
-
注意 :
-
①、
sleep()
是 静态方法 ,最好不要用 Thread 的实例 对象 调用它,因为它睡眠的始终是当前正在运行的线程,而不是调用它的线程对象,它只对正在运行状态的线程对象有效。 -
②、Java线程调度 是 Java 多线程的 核心 , 但是不管程序员怎么编写调度,只能最大限度的影响线程执行的 次序 ,而不能做到精准控制。因为
sleep() -> 阻塞状态 -> 就绪状态 -> 运行状态。
而就绪状态进入到运行状态,是由系统控制的
-
①、
-
-
3.2、
线程礼让——yield()
-
性质:
- 一个静态的方法
-
yield()
方法只是让当前线程 暂停 一下,重新进入就绪的线程池中,让系统的线程调度器重新调度器 重新调度一次,所以有可能刚进入就绪状态,又被调度到运行状态 - 让出 cpu 资源给其他的线程。
-
与
sleep()
的区别-
①、
sleep()
方法暂停当前线程后,会进入 阻塞 状态,只有当睡眠时间到了,才会转入 就绪 状态。
而yield()
方法调用后,是直接进入 就绪 状态,所以有可能刚进入就绪状态,又被调度到 运行 状态。 -
③、
sleep()
声明抛出了InterruptedException
,所以调用sleep()
方法的时候要捕获该异常,或者显示声明抛出该异常。
而yield()
方法则没有声明抛出任务异常。 -
③、
sleep()
方法比yield()
方法有更好的 可移植性 ,通常 不要依靠yield()
方法来控制并发线程的执行。
-
①、
-
-
3.3、
线程合并 (插队)——join()
-
性质:线程
1
使用join()
后,线程2
必须等待线程 1 执行完毕才能执行!,常常在一个线程的内部的run()
方法,进行另一个线程的join()
(插队) -
重载版本
-
void join();
当前线程等该加入该线程后面,等待该线程终止。 -
void join(long millis)
- 当前线程等待该线程终止的时间最长为 millis 毫秒。如果在 millis 时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待 cpu 调度。
-
void join(long millis,int nanos)
- 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。如果在 millis 时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待 cpu 调度
-
-
性质:线程
-
3.4、
线程优先级 ——Priority
-
性质:
- 每个线程执行时都有一个优先级的属性
- 优先级低只是意味着 获得调度 的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。
- 与 线程休眠 类似,线程的优先级仍然无法保障线程的 执行次序。
- 每个线程默认的优先级都与创建它的父线程具有 相同 的优先级,在默认情况下,
main
线程具有普通优先级
-
使用:
Thread
类提供了-
setPriority(int newPriority)
设置 1 个指定线程的优先级 -
getPriority()
返回 1 个指定线程的优先级 - 范围是 1 ~ 10 之间
- 推荐使用
Thread
类的静态成员变量进行设置,这样才能保证程序最好的可移植性。
MAX_PRIORITY = 10
MIN_PRIORITY = 1
NORM_PRIORITY = 5
-
-
-
3.5、
守护线程-
性质:
- 它为用户线程提供后台支持任务的服务。- 它在生命中没有为服务用户线程而发挥作用。- 它的生命取决于用户线程。它是一个低优先级的线程。- ** 用户线程都执行完,JVM 会终止守护程序线程 **
-
应用实例(守护线程通常用于执行一些后台作业)
-
JVM
的垃圾回收、内存管理等线程都是守护线程 - 数据库连接池,连接池本身也包含着很多后台 (守护) 线程,监控连接个数、超时时间、状态等等。
- 应用程序运行时播放背景音乐,在文字编辑器里做自动语法检查、自动保存等功能。
- 如果将这个播放背景音乐的线程设定为 非守护线程,那么在用户请求退出的时候,不仅要退出主线程,还要通知播放背景音乐的线程退出;如果设定为守护线程则不需要了
-
-
使用方法
`public final void setDaemon(boolean on);` Thread t = new Thread(() -> {public void run() {//do something;} }); t.setDaemon(true); // 该方法必须在 start() 启动线程前调用。t.start();
-
可能的异常
IllegalThreadStateException - 如果该线程处于活动状态。SecurityException - 如果当前线程无法修改该线程。
-
-
-
3.6、
如何高效地结束一个线程!-
Tip:
Thread.stop()、Thread.suspend、Thread.resume、Runtime.runFinalizersOnExit
这些终止线程运行的方法已经被废弃了,使用它们是极端不安全的! -
常用方法
-
1、正常执行完
run()
方法,然后结束掉class MyThread extends Thread { int i=0; @Override public void run() {while (true) {if (i == 10) break; i++; System.out.println(i); } } }
-
2、控制循环条件和判断条件的标识符来结束掉线程
class MyThread extends Thread { int i = 0; boolean next = true; @Override public void run() {while (next) {if(i == 10) next = false; i++; System.out.println(i); } } }
-
-
Tip:
-
3、使用
interrupt()
这个巧妙的方式结束掉这个线程-
3.1
第 2 种方法的标识符来结束一个线程,是一个不错的方法,但如果,该线程是处于sleep()、wait()、join()
的状态的时候,while
循环就不会执行,那么我们的 标识符 就无用武之地了, -
3.2
我们看看sleep()、wait()、join()
方法的声明:public final void wait() throws InterruptedException public static native void sleep(long millis) throws InterruptedException public final void join() throws InterruptedException
- 三者有一个共同点,都抛出了一个 InterruptedException 的异常。
-
3.3
在什么时候会产生这样一个异常呢?- 当一个线程处于 sleep()、wait()、join() 这三种状态之一的时候,如果此时它的中断状态为
true
,那么它就会抛出一个InterruptedException 的异常,并会将中断状态重新设置为false
。
- 当一个线程处于 sleep()、wait()、join() 这三种状态之一的时候,如果此时它的中断状态为
-
3.4 例子
public class Test {public static void main(String[] args) throws InterruptedException {MyThread thread=new MyThread(); thread.start();} } class MyThread extends Thread { int i = 1; @Override public void run() {while (true) {System.out.println(i); System.out.println(this.isInterrupted()); try{System.out.println("我马上去 sleep 了"); Thread.sleep(2000); this.interrupt(); //interrupt()方法将中断状态设置为 true } catch (InterruptedException e){System.out.println("异常捕获了" + this.isInterrupted()); return; } i++; } } }结果:1. 1 2. false 3. 我马上去 sleep 了 4. 2 5. true 6. 我马上去 sleep 了 7. 异常捕获了 false (1)首先执行第一次 while 循环,在第一次循环中,睡眠 2 秒,然后将中断状态设置为 true。(2)当进入到第二次循环的时候,中断状态就是第一次设置的 true,当它再次进入 sleep 的时候,马上就抛出了 InterruptedException 异常 (3)然后被我们捕获了。(4)然后中断状态又被重新自动设置为 false 了(从最后一条输出可以看出来)
-