企业搭建网站的必要性,企业网站建设东莞,重庆用百度还是高德地图,商务推广网站目录
1、run start 2、Thread类常见的属性和方法
2.1 构造方法
2.2 属性
3、后台进程 前台进程
4、setDaemon
5、isAlive
6、终止一个线程
6.1 变量捕获
6.2 currentThread isInterrupted interrupt 1、run start
在多线程#xff08…目录
1、run start 2、Thread类常见的属性和方法
2.1 构造方法
2.2 属性
3、后台进程 前台进程
4、setDaemon
5、isAlive
6、终止一个线程
6.1 变量捕获
6.2 currentThread isInterrupted interrupt 1、run start
在多线程一的博客中为大家介绍了五种创建线程的方式但从大类上分也就是两大类——分别是通过自定义类继承Thread以及自定义类实现Runnable接口(解耦合)来实现。
这两种方式中都用到了start方法以及run方法这里再次强调一下两种方法的作用及区别。
start方法的作用是真正在系统中创建线程并启动线程(由JVM调用操作系统的api完成线程创建操作)。start是jvm提供的方法本质上是调用操作系统提供的api。start是native修饰的本地方法说明是在JVM内部实现的并由C代码实现JVM由C实现 每个Thread对象都只能start一次也就是说一个Thread对象只能创建一个线程。每想创建一个新的线程就需要创建一个新的Thread对象。“日抛”一次性的run方法是线程的入口方法不需要手动调用当新的线程启动后就会自动调用。run方法相当于一个“回调函数”。什么是“回调函数”简单来说就是这个方法自己不用让别人去用。举个例子我们使用优先级队列(堆)时需要指定比较规则其中Comparable的compareTo方法以及Comparator的compare方法就是回调函数。 2、Thread类常见的属性和方法
2.1 构造方法 相信大家对于前两个构造方法并不陌生就是我们上文所提到创建线程的两种方法。 而第三种和第四种则是额外给创建的线程自定义名字。
给线程起名字其作用就是方便我们程序员调试(通过名字描述线程的作用)哪怕线程的名字相同也不会影响线程的执行。 如下图所示
我们就可以观察到我们自定义的两个名为t1、t2的两个线程正在运行。
但是我们并没有发现主线程main这是为什么呢
——这是因为主线程中已经结束了(main中并没有死循环代码主线程main早已执行完毕了~~)。
我们之前的认知时main是程序的入口main执行完程序(进程)就执行完毕了其实这只是针对于单线程程序来说的。在多线程中非也~~
我们当然也可以通过写死循环代码来观察主线程main。 public class Demo6 {public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(() - {while (true) {System.out.println(hello t1);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}, t1);t1.start();Thread t2 new Thread(() - {while (true) {System.out.println(hello t2);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}, t2);t2.start();while (true) {System.out.println(hello main);Thread.sleep(1000);}}
} 2.2 属性 ID就类似于pid是线程的唯⼀标识不同线程不会重复。名称即我们给线程自定义的名字没有自定义则为系统默认的名字。状态优先级这里就不再赘述。
线程中含有多个属性我们可以通过Thread中相应的方法进行获取。 3、后台进程 前台进程 isDaemon方法用来判断线程是否为 后台线程。
我们需要先了解 前台线程 与 后台线程。
前台线程 的作用大能够直接影响进程是否执行完毕。当所有的前台线程都执行完后进程才能结束要是有一个前台线程没有结束整个进程都不能结束。
我们自己创建的线程包括main主线程都是前台线程。
后台线程又称为 守护线程Deamon就是守护的意思。说明这个线程 是默默守护的他们的存在不会影响 进程 的结束。
一些JVM自带的线程就是后台线程他们的存在左右不了进程的结束。简单来说如果进程要结束不会问这些后台线程的意见进程要是想结束那他们就必须跟着结束进程结束后他们也会随之结束。
有些后台线程存在的意义不是很大即使没有他们也没啥影响。但是有些后台线程是JVM提供的一些具有特殊功能的线程会跟随整个进程持续执行(不能随便离开)比如垃圾回收进程。 前台线程 和 后台线程 都是有多个的。
如果有多个前台线程必须所有的 前台线程 结束后进程才能结束。
4、setDaemon
我们可以通过setDaemon方法将 前台线程 修改为 后台线程。
注意必须在线程创建前修改即调用start方法前使用setDaemon方法修改
将 前台线程 修改为 后台线程 后这些线程将无力阻止进程的结束。 此时仅有一个前台线程main当main结束后整个进程将会结束虽然t1处于循环状态但t1为后台线程不会影响整个进程的状态。 进程间存在 父子关系 但线程间不存在。 比如 IDEA 本身就是一个进程打开 IDEA 后运行的Java代码又是一个进程这就是进程间的父子关系。 5、isAlive isAlive方法用来判断线程是否存活。
在Java中Thread创建的对象和线程是一一对应的关系。
但是Thread对象的生命周期和所创建的线程的生命周期是不同的可能存在线程已经结束但是Thread对象依旧存活的情况。 我们将线程设置为存活3s但是发现却打印出了4个true这是由于并发时操作系统随机调度的原因主线程的第四次打印和thread线程结束谁先谁后是不一定的~~
在这里就是在主线程第四次打印true时(主线程先被调度执行)thread线程还有一口气但是马上就无了~~ 6、终止一个线程
终止 指的是让一个线程结束且不会再恢复了。
当一个线程的入口方法执行完毕后线程也就随即结束了。
那么让一个线程提前终止其实就是让run方法尽早return而已。
6.1 变量捕获
我们可以通过变量设置控制线程的存活时间。 此时可以得到我们想要的结果。
此时的isFinish为成员变量但是当我们把isFinish放到main方法中当做局部变量时却发生了报错这是为什么呢 它告诉我们isFinish应该是final类型或者事实上为final类型的也就是说isFinish的值不能发生改变。
这是因为在lambda内部触发了“变量捕获”的语法而我们在下面的代码中对isFinish进行了修改所以发生了报错。
因为lambda本来就是用于“回调函数”使用的而 回调函数 执行时机是不确定的可能是很久以后才进行线程的创建而此时main这个主线程可能早已经执行完毕了那么main内部的局部变量早已销毁。
为解决上述问题Java将被捕获的变量拷贝了一份传到了lambda的内部以此让lambda能够使用外面的局部变量。但是拷贝的变量就意味着这个变量和原本的变量不是同一个东西当一方进行修改时另一方不会随之改变。而这边变那边又不变就会给程序猿带来更多的问题~~
为解决这个问题Java大佬们这样决定就根本不允许这个变量进行修改。这就是“变量捕获”的语法被捕获的变量必须是final或事实上为final的(不是final类型但是变量值没有改变)。
而当我们将isFinish设置为成员变量时此时就不再是“变量捕获”语法的范畴而切换为“内部类访问外部类成员”的语法(lambda本来就是实现函数式接口的匿名内部类的简化形式本质上就是一个内部类)而“内部类访问外部类成员”的语法本来就是正确的。 对象本体以及其中的成员变量的生命周期都是由GC(垃圾回收)进行管理的不会随方法的结束而销毁。 当final修饰引用类型时不能修改引用的指向(不能将这个引用指向别的对象)但是可以修改引用指向的对象本体。 6.2 currentThread isInterrupted interrupt
isInterrupted方法的作用就是判断线程是否被终止。 lambda表达式的执行是在对象new之前此时的thread引用还没有被声明。
我们需要使用Thread的静态方法currentThread获取线程的引用。 currentThread方法在哪个线程内部被调用获取的就是哪个线程的Thread引用。而这个代码是在 lambda 中 (也就是在 thread 线程的入口run方法中) 调用的获取的就是 thread 线程的引用。类似于this
故 Thread.currentThread().isInterrupted() 的作用就是判断线程是否被终止了。
interrupt方法的作用是去主动终止线程修改内部的boolean变量(标志位)值 当我们设置好终止线程的方法后运行发现有异常抛出被JVM捕获导致程序直接终止
这是由于调用interrupt方法的原因
interrupt方法不仅会修改线程内部的boolean变量值还会唤醒类似于sleep这样的阻塞方法使其抛出异常若catch块内没有将异常处理好则会直接带走整个进程 而我们不想让线程掀桌我们可以修改 IDEA 默认的处理异常的方式使用 break 替换使线程更加优雅的终止
虽说是优雅了但是依然是interrupt唤醒了sleep抛出异常通过catch逻辑来进行的线程终止操作 public static void main(String[] args) throws InterruptedException {Thread thread new Thread(() - {//这个代码是在 lambda 中 (也就是在 thread 线程的入口run方法中) 调用的获取的就是 thread 线程的引用while (!Thread.currentThread().isInterrupted()) {try {System.out.println(hello thread);Thread.sleep(1000);} catch (InterruptedException e) {// 唤醒sleep抛出异常catch捕获后再次抛出新异常// 线程的 “掀桌” 操作//throw new RuntimeException(e);break;}}System.out.println(thread 结束);}, thread);thread.start();Thread.sleep(3000);// 主动终止线程System.out.println(main 尝试终止线程thread);thread.interrupt();} END