背诵 Java多线程

1、进程
进程是系统进行资源分配和调度的独立单位,每一个进程都有它自己的内存空间和系统资源;进程实现多处理机环境下的进程 调度、分派、切换时,都需要花费较大的时间和空间开销;为了提高系统的执行效率,减少处理机的空转时间和调度切换的时间,以及便于系统管理,所以有了线程,线程取代了进程调度的基本功能;简单来说,进程作为资源分配的基本单位,线程作为资源调度的基本单位

2、进程之间的通信方式
进程间通信主要有以下7种方式:管道/匿名管道、有名管道、信号、消息队列、共享内存、信号量、Socket

3、Java线程通信方式
① wait()、notify()、notifyAll():wait()方法可以让当前线程释放对象锁并进入阻塞状态;notify()方法用于唤醒一个正在等待相应对象锁的线程,使其进入就绪队列
② await()、signal()、signalAll():使用Condition 的 await() + signal()/signalAll() 这种方式能够更加安全和高效地实现线程间协作
③ BlockingQueue:程序的两个线程通过交替向BlockingQueue中放入元素、取出元素,即可很好地控制线程的通信

4、创建线程的四种方式
1、继承Thread类
2、实现Runnable接口
3、实现Callable接口
4、使用线程池创建
一般推荐采用实现接口的方式来创建多线程

5、线程中start()和run()的区
start() ;它的作用是启动一个新线程,start()不能被重复调用
run()方法称为线程体,它包含了该线程要执行的内容,run()方法运行结束,此线程随即终止;和普通的成员方法一样,可以被重复调用
注意:如果直接调用run方法,并不会启动新线程!

6、线程的生命周期和状态
① 新建:线程对象已经创建,但是还没有调用start()方法
② 就绪:线程对象调用start()方法,但是还未开始运行就是就绪状态;一旦获取CPU时间片,就开始运行
③ 运行:当线程对象调用了start()方法,并且获取了CPU时间片,就是运行状态
③ 阻塞:等待获取一个排它锁,如果其线程释放了锁就会结束此状态;如调用wait()方法进入阻塞状态
⑥ 死亡:可以是线程结束任务后自己结束,或者产生了异常而结束

7、线程死锁
死锁:两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象

产生死锁的4个必要条件:
① 互斥条件:某一资源在任意⼀个时刻只由⼀个线程占⽤
② 请求与保持条件:⼀个线程因请求资源⽽阻塞时,对已获得的资源保持不放
③ 不可剥夺条件:线程已获得的资源在末使⽤完之前不能被其他线程强⾏剥夺,只有⾃⼰使⽤完毕后才释放资源
④ 循环等待条件:若⼲线程之间形成⼀种头尾相接的循环等待资源关系

避免死锁最简单的方法就是 破坏循环等待条件 ,将系统中所有的资源设置标志位、排序, 规定所有的进程申请资源必须以一定的顺序(升序或降序)申请

8、sleep() 和 wait() 的区别

  • sleep()是属于Thread类中的方法;让当前正在执行的线程在指定的时间内暂停执行,进入阻塞状态,但是在此期间线程不释放锁,只阻塞线程,当指定的时间到了又会自动恢复运行状态,可中断,sleep()给其他线程运行机会时不考虑线程的优先级,高优先级和低优先级的线程都有机会执行

  • wait()是属于Object 类中的方法,线程会释放对象锁,只有当其他线程调用 notify() 或 notifyAll() 才能唤醒此线程wait()方法在使用时必须先获取对象锁,即必须在 synchronized 修饰的方法或代码块中使用,那么相应的 notify() 或 notifyAll() 方法同样必须在 synchronized 修饰的代码块中使用,如果没有在 synchronized 修饰的代码块中使用时运行时会抛出IllegalMonitorStateException的异常

9、wait()方法为什么要在while()循环中使用?

10、线程池
线程池就是首先创建一些线程,它们的集合称为线程池;使用线程池可以很好地提高性能;
程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务

11、线程池的工作机制
在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程;一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务

12、什么是线程复用?线程复用的原理
线程复用:在线程池中,通过同一个线程去执行不同的任务
线程复用的原理:在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其核心原理在于线程池对 Thread 进行了封装,并不是每次执行任务时都会调用 Thread.start() 来创建新线程,而是让每个线程去执行一个“循环任务”,在这个“循环任务”中不停的检查是否有任务需要被执行,如果有则直接执行,也就是调用任务中的 run()方法,将 run()方法当成一个普通的方法执行

总结:线程池将线程和任务进行解耦,摆脱了之前通过 Thread 创建线程时的一个线程必须对应一个任务的限制

13、为什么使用线程池
(1) 降低资源消耗;通过重复利用已创建的线程降低线程创建和销毁造成的消耗
(2) 提高响应速度;当任务到达时,任务可以不需要等到线程创建就能立即执行
(3) 提高线程的可管理性;线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控

14、线程池有哪些参数?每个参数的作用是什么?

序号 参数 含义 解释
1 corePoolSize 核心工作线程数 当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize时
2 maximumPoolSize 最大线程数 线程池所允许的最大线程个数;当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。另外,对于无界队列,可忽略该参数
3 keepAliveTime 多余线程存活时间 当线程池中线程数大于核心线程数时,线程的空闲时间如果超过线程存活时间,那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数
4 unit 空闲线程存活时间单位 keepAliveTime的计量单位
5 workQueue 工作队列 用于传输和保存等待执行任务的阻塞队列;即任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务
6 threadFactory 线程创建工厂 用于创建新线程;threadFactory创建的线程也是采用newThread()方式,threadFactory创建的线程名都具有统一的风格: pool-m-thread-n (m为线程池的编号,n为线程池内的线程编号)
7 handler 拒绝策略 当线程池和队列都满了,再加入线程会执行此策略

15、线程池的拒绝策略和阻塞队列

posted @   紫薇哥哥  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程
点击右上角即可分享
微信分享提示