目录
一、多线程
1、线程的五种状态
2、什么是线程、进程?
进程是资源分配最小单位,线程是程序执行的最小单位
什么是进程:
1)cpu从硬盘中读取一段程序到内存中,该执行程序的实例就叫做进程
2)一个程序如果被cpu多次被读取到内存中,则变成多个独立的进程什么是线程:
是一条程序的执行路径
线程是程序执行的最小单位,在一个进程中可以有多个不同的线程同时执行如:一台电脑安装一个exe文件就是一个应用程序,也就是一个进程,但是可以多个用户进行使用访问,那么每个用户的访问就会生成一个线程去执行业务代码。
故而进程是资源分配的最小单位,线程是程序执行的最小单位。【问:进程与线程的区别?电脑上安装的exe文件就是一个应用程序,也就是进程。后端服务中,有很多接口,用户的每次访问,会调用一个或多个接口,在不考虑异步和多线程的情况下,所有接口会按顺序执行,那么这一条执行路径就是我们所说的线程。在一个进程中,可以有多个线程同时执行。总之,进程是资源分配的最小单位,线程是程序执行的最小单位。】
3、什么是多线程呢?
产生背景:默认的情况下,HTTP协议采用同步的形式,并基于请求与响应的过程。如果服务器没有及时响应给客户端,会造成客户端一直等待,用户体验非常不友好。
作用:耗时且不需要获取其结果的代码,可以开启另一个线程去执行,提高响应速度和程序的效率。
4、单线程与多线程执行有哪些区别(并行&串行)?
单线程(串行、同步)执行代码的顺序是从上往下,如果中间某个环节出错,整个程序直接报错中断;
多线程(并行、异步)采用多条不同的线程执行代码,每个线程互不影响,提高响应速度。
5、CPU切换线程的概念
对于使用单核CPU服务器,在开启多线程的情况下,并不是真正意义上的多线程。因为单核CPU服务器在同一时刻只能执行一个线程,当由一个线程切换执行另一个线程的过程,称为CPU切换。
——CPU每次计算的时间为一个CPU时间片,实际只有几十毫秒人为感觉好像是在多线程。
——等待CPU调度的时候,该线程的状态为就绪状态,如果被CPU调度则该线程的状态为运行状态,当cpu转让执行其他的线程时,则该线程又变为就绪状态。
——CPU密集型:长时间占用CPU IO密集型 :CPU计算时间短 访问外接设备时间
① 什么是CPU切换:CPU从一个线程到切换执行另一个线程的过程。
② 什么是CPU时间片:CPU每次执行的时间为一个CPU时间片。
③ 切换前后线程的状态:被CPU执行的线程t1为运行状态;CPU切换之后该线程t1变为就绪状态,再次等待CPU的调度。
6、使用多线程一定提高效率吗?
不一定。如果频繁开启多线程,会导致CPU不断切换,影响服务器的性能。
如果是一个磁盘或网络为主的应用程序(IO密集型程序),我们知道IO的速度比起 CPU 来是很慢的,很容易出现线程在进行IO,CPU闲着没事干,这个时候使用多线程可以让CPU跑起来,提高应用程序的效率。
如果项目比较小,建议采用多线程异步实现;如果项目比较大,建议使用MQ异步实现。
7、多线程的应用场景有哪些呢
--异步发送短信/发送邮件
--异步记录日志 日志框架底层
--后端比较耗时间的代码采用异步执行
--支付异步回调
统一思想:异步的目的是为了将执行比较耗时的代码,改用多线程异步执行,提高HTTP协议的响应速度。
8、多线程的创建方式
1)继承Thread类,重写run方法
2)实现Runnable接口
3)可以带回返回结果的线程Callable
4)线程池
5)Spring提供异步注解@Async
8.1 继承Thread类创建线程
public class ThreadDemo01 extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "<run>");
}
public static void main(String[] args) {
// 创建一个线程
ThreadDemo01 threadDemo01 = new ThreadDemo01();
// 启动线程是start方法而不是run方法
threadDemo01.start();
}
}
8.2 实现Runnable接口创建线程
public class ThreadDemo02 implements Runnable {
public void run() {
System.out.println(Thread.currentThread().getName() + ",我是子线程");
}
public static void main(String[] args) {
new Thread(new ThreadDemo02()).start();
}
}
8.3 实现Callable接口创建多线程
采用异步执行线程,但是如果要获取该线程结果的情况下,整个代码的执行顺序就变成了单线程。
8.4 使用线程池的方式创建线程
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ">我是子线程<");
}
});
8.5 @Async异步注解创建线程
Spring @Async 底层基于aop+自定义注解实现。
@RequestMapping("/addOrder")
public String addOrder() {
log.info("<1>");
orderManage.asyncLog();
log.info("<3>");
return "3";
}
@Component
@Slf4j
public class OrderManage {
@Async
public void asyncLog() {
try {
Thread.sleep(3000);
log.info("<2>");
} catch (Exception e) {
}
}
}
9、什么是线程安全问题
当多个线程同时共享同一个全局变量,同时做写的操作时,可能会受到其他的线程干扰,发生数据冲突,也就是线程安全问题。
关键词:同时、写操作
10、如何解决线程安全问题
核心思想:在多线程的情况下,同一时刻,保证只能有一个线程去执行可能产生线程安全的代码(串