【java基础】多线程详解 (全干货)
如有错写或表述不清楚的地方,欢迎各位大佬指摘
写这篇文档主要是因为博主准备跳槽了,面试不可缺少的八股文,自己也正在重新回顾,就把这些东西记录下来,希望对其他需要面经的人有帮助
1、进程与线程
刚开始学习线程的同学,可能会有人弄混这俩个名词
进程:可以看作我们经常打的游戏,王者荣耀是个进程,原神是个进程,这两个进程都是独立的,互不影响,王者输了对原神的抽卡没有任何影响
线程:线程又分为单线程与多线程,单线程就比如我们在跑图的时候不能同时去抽卡,只能按顺序去执行,多线程就比如地铁的闸机口,可以在第一个口进,也可以在第二进,不需要互相等待,所以多线程是可以提高我们程序的处理效率的
2、线程提交任务的流程
我们先了解一下线程提交任务的流程,有个大概了解后,我们在去讲解线程的生命周期与线程的实现方法
流程图如果大家看的不明白的话,给大家大白话描述一下(本博的表达能力真的很…),有兴趣的小伙伴也可也看下 execute() 方法中的代码处理逻辑
1.提交一个Runnable
2.判断当前的线程数是否小于当前核心线程数
3.如果小于,那就创建一个新线程并执行Runnable
4.如果大于等于,那就将这个线程放入队列中
5.如果队列没有满,那就将Runnable放入,等待执行
6.如果队列满了,入队失败会尝试继续追加线程
7.判断当前线程数是否小于最大线程数
8.如果小于,那就创建线程并执行任务
9.如果大于等于,那就拒绝执行此Runnable(其实就是抛异常了)
3、线程的生命周期
线程被创建并启动后,他不是一启动就进入了执行状态,也不是一直处于执行状态,在线程的生命周期中,他要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)五种状态,当线程启动后,他不会一直霸占着CPU独自运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换
4、线程的实现方式
在java中线程的创建方式有两种,一个是继承java.lang.Thread类,一个是实现java.lang.Runnable接口
4.1、第一种方式继承Thread类
Thread其实本质上还是实现了Runnable接口的一个实例
启动线程的唯一方式是通过调用线程对象的start(),他将启动一个新线程,并执行run()方法
示例代码:
class MyThread extends Thread {
public void run() {
// 线程任务逻辑
}
}
public class Main {
public static void main(String[] args) {
// 新建线程
MyThread thread = new MyThread();
// 不会启动线程,只是普通的调用方法,就是单线程
// thread.run();
// 启动线程
thread.start();
}
}
问题:start()与run()的区别
1.thread.start()方法是启动线程,在jvm中开辟了一个新的栈空间,真正实现了多线程运行,这时不需要等待run()方法执行完毕,就可以直接继续调用下面的方法,此时线程是处于就绪状态,并没有运行,线程启动成功后线程会自动调用run()方法
2.thread.run()方法称为线程体,包含了要执行的这个线程的内容,现在就进入了运行状态,开始运行run函数中的代码,run方法运行结束,此线程终止,然后CPU在调度其他线程
4.2、第二种方式实现Runnable接口
当自己的类以及继承另一个类就无法直接继承Thread ,此时可以实现一个Runnable接口,所以这种方式更灵活
class MyRunnable implements Runnable {
public void run() {
// 线程任务逻辑
}
}
public class Main {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
// 创建线程
Thread thread = new Thread(runnable);
// 启动线程
thread.start();
}
}
使用匿名内部类创建:
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
public void run() {
// 线程任务逻辑
}
});
// 启动线程
thread.start();
}
}
5、关于线程的结束方法(DEAD)
线程会以下面四种方式结束,结束后就是死亡状态
5.1、正常结束
显而易见就是线程正常结束,run()或call()方法执行完毕
5.2、异常
线程抛出一个未捕获的Exception
5.3、Stop方法终止(不推荐,了解就可)
直接调用线程的stop()方法来结束该线程 -------- 该方法通常容易导致死锁,不推荐!!!!!!!
此方法就像突然拔掉了电脑电源,可能会产生不可预料的结果
5.3、interrupt方法终止(常用)
public class MyThread extends Thread {
public void run() {
try {
// 线程执行的代码
// ...
// 在执行过程中检查中断状态
if (Thread.interrupted()) {
throw new InterruptedException();
}
// 继续执行剩余的代码
} catch (InterruptedException e) {
// 处理中断异常
}
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
// 中断线程
thread.interrupt();
}
}
由上述代码所示,interrupt方法来中断线程有两种情况
1.线程处于阻塞状态,比如使用了sleep(),当调用线程interrupt()方法时,会抛出InterruptedException 异常,通过代码捕获改异常后,然后可以使用break跳出循环状态,从而让我们有机会结束这个线程的执行,通常很多人认为只要调用interrupt()方法线程就会结束,其实是错的,一定要先捕获InterruptedException 异常后通过break跳出循环从而正常结束run方法
2.非阻塞状态,使用interrupted()来判断线程的终止标志来退出循环,当使用interrupt()时,中断标志就会置为true
未完…待续…