每日速记10道java面试题07
其他资料:
目录
7.java线程池中shutdown和shutdownNow的区别?
8.Java 中的 DelayQueue 和 ScheduledThreadPool 有什么区别?
1.线程的生命周期在java中是怎样的?
在 Java 中,线程的生命周期可以细化为以下几个状态:
New(初始状态):线程对象创建后,但未调用 start0方法。
Runnable(可运行状态):调用 start()方法后,线程进入就绪状态,等待 CPU 调度。
Blocked(阻塞状态):线程试图获取一个对象锁而被阻塞。
Waiting(等待状态):线程进入等待状态,需要被显式唤醒才能继续执行。
Timed Waiting(含等待时间的等待状态):线程进入等待状态,但指定了等待时间,超时后会被唤醒。
Terminated(终止状态):线程执行完成或因异常退出。
2.java中如何创建多线程?
1)继承Thread类:用户自定义继承Java.lang.Thread类,重写run方法,在run方法里面定义线程的具体实现,创建该类的实例后,通过start方法启动线程。
优点:编写简单
缺点:继承了Thread类,所以不能再继承其他类,功能比较局限
2)实现Runnable接口:用户自定义实现java.lang.Runnable接口,重写run方法,此后将runnable对象作为参数传递给thread类的构造器,创建thread对象后再调用start方法启动线程。
优点:可以继承其他的类,而且在这种方式下,可以多个线程共享同一个目标对象,非常适合多个相同线程处理同一份资源的情况,体现了面向对象的思想。
缺点:相比较第一种方法编程略微复杂。
3)实现callable接口与FutureTask:Callable接口与runnable类似,但callable的call方法可以有返回值且可以抛出异常,要执行callable任务,需要把他包装进一个FutureTask中来实现。
优点缺点也是和runnable类似,优点是可以实现多线程处理同一份资源,缺点是编程稍微复杂。
4)使用线程池(Executor框架):可以通过Executors类的静态方法创建不同类型的线程池。
优点:线程池可以重用预先创建的线程,避免了线程创建和销毁的开销,提高了程序的性能;而且通过合理配置线程池大小,可以最大化CPU利用率和系统吞吐量。
缺点:增加了程序的复杂度。
延伸→这边面试官可能就会问你关于线程池的问题了。
3.你了解java线程池的原理吗?
线程池使用原理:先使用核心线程数量,当核心线程数量用完后,使用队列存储线程,不同的线程池内置的队列长度不同,存在无限制长的队列也存在容量为0的队列,当队列长度消耗光的时候会去使用最大线程池数量,最大线程池数量用完后会触发线程池拒绝策略,一共有4种拒绝策略,默认抛出异常,第二种会将当前任务交给父线程执行,阻塞向线程池添加的速度,第三种是抛弃最早的线程重新加入线程队列,第四种是直接抛弃新来的线程调最早的线程。
用通俗易懂的话来理解就是:
线程池可以理解为,去银行办理业务:默认有6个柜台,当没有人去银行办理业务时候,柜台小姐姐都是拉呱玩耍的,有人去办理业务,先开三个柜台来办理业务,若三个柜台都有人在办理业务,那来的人先去等候区,若此时等候区也满了,就去新开另外三个柜台,随着办理业务人员增多,新开的柜台也都有人在办理业务,排队等候区也满了,那你还来办理业务??想搞事呢??明天再来或者去前面找个人插队,把第一个等候区的人赶走……,这就是经理的拒绝策略。
延伸→这时候面试官可能会问:换做是你,你会如何设置java线程池的数量?
4.换做是你,你会如何设置java线程池的数量?
对于不同类型的任务,线程数设置不同
如果是cpu密集型的任务,一般i/o开销较小,可以充分利用cpu资源,线程数就是cpu核心数+1
IO密集型任务,会因为i/o阻塞,导致无法高效利用cpu,如果任务较多,就需要更多的线程,一般就是cpu核心数*2
以上的公式都只是一个理论值,实际情况还得具体情况具体分析,例如机器性能、预期CPU利用率等等,并不是写死的一个数。
5.java线程池有哪些拒绝策略?
abort: 默认。抛异常。适用于要通知调用者任务没有被执行的场景。
callerRuns:调用者处理任务,哪个线程提交的任务,哪个线程自己执行。
DiscardOldest:扔掉任务队列里面排队时间最长的。适用于丢弃老的任务,处理重要的新任务的场景。
discard: 直接静悄悄的扔掉。适用于丢掉任务没有任何影响的场景。
6.java并发库中提供了哪些线程池?
Executors类提供五种静态工厂方法用于创建不同类型的线程池。
固定线程池的核心和最大线程数相同,队列无界,适合于数量确定的稳定任务,但可能导致内存溢出。
工作窃取池是在JDK8引入的,利用ForkoinPool实现,能在处理完自己的任务后去其他线程的队列中窃取任务。
单线程池中只有一个线程,能按顺序执行任务,适合需要顺序执行的场景。
缓存线程池的核心线程数为0,最大线程数可以无限,适合短时间大量短任务的场景,但可能导致频繁的上下文切换。
计划线程池用于需要定时或周期性执行任务,底层使用DelavedWorkQueue实现延时任务。
7.java线程池中shutdown和shutdownNow的区别?
1.shutdown会关闭线程池,拒绝接收新的任务,会把任务队列中的任务执行完再关闭。适用于程序需要平滑停止线程池的场景,如应用程序正常退出时。保证所有已提交的任务都能执行完毕,避免任务丢失。
2.shutdownNow:强制关闭线程池,将任务队列的任务返回,清空任务队列,强制中断当前执行的任务,但是不一定保证百分百中断成功 。适用于紧急情况或需要立即停止线程池的场景,如应用程序异常退出时。快速清理资源,但可能导致部分任务未完成。
shutdown就好比餐厅快打烊了,拒绝接新客,但会把已经在店的客人招待完了才关门。
shutdownNow就好比餐厅着火了,直接把客人们赶走关门了,但可能会导致部分客人没吃完饭。
8.Java 中的 DelayQueue 和 ScheduledThreadPool 有什么区别?
DelayQueue 是一个阻塞队列,而 ScheduledThreadPool是线程池,不过内部核心原理都是差不多的。
DelayQueue 是利用优先队列存储元素,当从队列中获取任务的时候,如果最老的任务已经到了执行时间,可以从队列中出队一个任务,反之可以获得 null 或者阻塞等待任务到时。
ScheduledThreadPool内部也使用的一个优先队列 DelayedWorkQueue 且可以内部多线程执行任务,支持定时执行的任务,即每隔一段时间执行一次的任务。
9.你了解java中的读写锁吧?
读写锁,它允许多个线程同时读取共享资源,而在写操作时确保只有一个线程能够进行写操作(读读操作不互斥,读写互斥、写写互斥)。这种机制适合于读多写少的场景,因为它提高了系统的并发性和性能。Java 中的 ReadwriteLock 是通过 ReentrantReadwriteLock 实现的,它提供了以下两种锁模式:
读锁(共享锁)允许多个线程同时获取读锁,只要没有任何线程持有写锁。适合读操作频繁而写操作较少的场景。
写锁(独占锁)写锁是独占的,当有线程持有写锁时,其他线程既不能获取写锁,也不能获取读锁。写锁用于保证写操作的独占性,防止数据不一致。
这里面试官大概率会延伸问读写锁的原理,如果没有自己能说出来就是加分!
10.说一说读写锁的原理
可以参考以下博文:读写锁详解_读写锁的实现原理-CSDN博客
原文地址:https://blog.csdn.net/csdn3043663729/article/details/144169644
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!