1.线程的常见构造方法
方法 | 说明 |
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用 Runnable 对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target, String name) | 使用 Runnable 对象创建线程对象,并命名 |
【了解】Thread(ThreadGroup group, Runnable target) | 线程可以被用来分组管理,分好的组即为线程组,这 个目前我们了解即可 |
2.线程的几个属性和方法
属性 | 获取方法 |
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
这里的后台线程和前台线程不同,当所有的前台线程执行完毕,即使后台线程还在工作中,也会直接自动退出.
只要前台线程没执行完,进程就不会结束,即使main结束了,前台线程也会继续执行
设置为后台进程
setDaemon()设为true就是后台,不设置就是默认前台
isAlive()表示内核中的PCB是否存在
这个对象的生命周期和PCB的是不完全一样的
因为我在创建对象之后这个PCB才存在,在执行完进程结束后之后这个PCB才销毁,而且java中的用户级线程不是直接映射到操作系统中的原生线程的
3.创建线程的5个方式
其实线程是操作系统提供的一种机制,并给用户提供了一些api供使用,Java的Thread类只是对其的进一步封装.
1.继承Thread类,重写run方法
2.重写runnable接口
3.使用匿名内部类实现Thread
4.使用匿名内部类实现Runnable接口
5.使用Lambda表达式
4.start()和run()方法的区别
说实话这两个方法是八竿子打不着的,为什么要把他们两个放进来比较呢?
可能有人认为start方法和run方法执行的是一件事情
其实这个理解是大错特错的,这两个方法完全不一样,我们举个例子
假设我们在调用main方法中调用一个run方法,你可能以为这样和使用start方法是一样的,其实不然,我们在run方法之前加上一个死循环,让run方法一直无法执行.而start方法却可以执行,因为start方法是重新创建了一个新的线程,不是在main线程中国运行的,所以不会受main线程中的死循环的干扰..
5.终止一个线程
package Thread;public class ThreadDemo12 {private static boolean isQuit = false;public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{while(!isQuit){System.out.println("我是一个线程,工作中");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("线程工作完毕");});t.start();Thread.sleep(3000);isQuit = true;System.out.println("让t线程退出");} }
最后结果为
这就好比这个时候我在打游戏,女朋友突然叫我去和她一起开心一下,这个时候就是main线程来中断我这个t线程,我得配合她她才能中断我的动作,假设我不配合,那她也没有办法.
所以这里的线程运行中想要中断,其实是需要两个线程中相互配合的,这里就使用了isQuit变量来实现其中的相互配合.
.
其实这里也有一个方法来实现对线程的中断,我们来尝试一下
interrupt()
isInterrupted() 判断是否被打断
我们不妨来试试用这个来作为标志位
package Thread;public class ThreadDemo13 {public static void main(String[] args) {Thread t = new Thread(()->{while(!Thread.currentThread().isInterrupted()) {System.out.println("我是一个线程,正在工作");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("线程执行完毕");});t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t线程退出");t.interrupt();} }
结果是
我们发现t线程并没有真正的执行结束,并且捕获了一个打断异常
结论:
sleep中的线程被打断之后,线程并没有直接退出,而是继续一直输出信息
这是因为线程在睡眠中被打断会抛出一个异常,并将isInterrupted重新设置为false
解决方案,在收到这个打断的异常之后,直接break跳出循环
有人会觉得还不如之前的isQuit标志位好,想在遇到这个异常信息的时候将isQuit设置为true,其实是行不通的,可以参考我上一篇文章中的变量捕获.因为匿名内部类中调用的局部变量只能是final修饰的或是事实final的
6.线程中的join方法
join方法能够实现线程的等待,假如在main线程中调用t.join,此时main线程就会等待t线程执行结束后再继续工作,t线程执行的时候,此线程属于阻塞状态
下面举个例子说明一下
package Thread;public class ThreadDemo14 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 5; i++) {System.out.println("马上到...");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("终于到了!!!");});t1.start();t1.join();System.out.println("我等你好久了~~~");}}
执行结果如下
注:start方法一定在join方法之前调用
此时main线程等待了t1线程执行完了才开始执行