2024年最新深入理解 Java 多线程核心知识:跳槽面试必备,超通俗解析CountDownLatch用法和源码

最后

最后,强调几点:

  • 1. 一定要谨慎对待写在简历上的东西,一定要对简历上的东西非常熟悉。因为一般情况下,面试官都是会根据你的简历来问的; 能有一个上得了台面的项目也非常重要,这很可能是面试官会大量发问的地方,所以在面试之前好好回顾一下自己所做的项目;
  • 2. 和面试官聊基础知识比如设计模式的使用、多线程的使用等等,可以结合具体的项目场景或者是自己在平时是如何使用的;
  • 3. 注意自己开源的Github项目,面试官可能会挖你的Github项目提问;

我个人觉得面试也像是一场全新的征程,失败和胜利都是平常之事。所以,劝各位不要因为面试失败而灰心、丧失斗志。也不要因为面试通过而沾沾自喜,等待你的将是更美好的未来,继续加油!

以上面试专题的答小编案整理成面试文档了,文档里有答案详解,以及其他一些大厂面试题目。

面试答案

三面头条+四面阿里+五面腾讯拿offer分享面经总结,最终入职阿里

三面头条+四面阿里+五面腾讯拿offer分享面经总结,最终入职阿里

三面头条+四面阿里+五面腾讯拿offer分享面经总结,最终入职阿里

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

活锁

假设有两个线程1、2,它们都需要资源 A/B,假设1号线程占有了 A 资源,2号线程占有了 B 资源;由于两个线程都需要同时拥有这两个资源才可以工作,为了避免死锁,1号线程释放了 A 资源占有锁,2号线程释放了 B 资源占有锁;此时 AB 空闲,两个线程又同时抢锁,再次出现上述情况,此时发生了活锁。简单类比,电梯遇到人,一个进的一个出的,对面占路,两个人同时往一个方向让路,来回重复,还是堵着路。如果线上应用遇到了活锁问题,恭喜你中奖了,这类问题比较难排查。

饥饿

饥饿是指某一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行。

线程的生命周期

在线程的生命周期中,它要经历创建、可运行、不可运行几种状态。

创建状态

当用 new 操作符创建一个新的线程对象时,该线程处于创建状态。

处于创建状态的线程只是一个空的线程对象,系统不为它分配资源。

可运行状态

执行线程的 start() 方法将为线程分配必须的系统资源,安排其运行,并调用线程体——run()方法,这样就使得该线程处于可运行状态(Runnable)。

这一状态并不是运行中状态(Running),因为线程也许实际上并未真正运行。

不可运行状态

当发生下列事件时,处于运行状态的线程会转入到不可运行状态:

  • 调用了 sleep() 方法;

  • 线程调用 wait() 方法等待特定条件的满足;

  • 线程输入/输出阻塞;

  • 返回可运行状态;

  • 处于睡眠状态的线程在指定的时间过去后;

  • 如果线程在等待某一条件,另一个对象必须通过 notify() 或 notifyAll() 方法通知等待线程条件的改变;

  • 如果线程是因为输入输出阻塞,等待输入输出完成。

线程的优先级

线程优先级及设置

线程的优先级是为了在多线程环境中便于系统对线程的调度,优先级高的线程将优先执行。一个线程的优先级设置遵从以下原则:

  • 线程创建时,子继承父的优先级;

  • 线程创建后,可通过调用 setPriority() 方法改变优先级;

  • 线程的优先级是1-10之间的正整数。

线程的调度策略

线程调度器选择优先级最高的线程运行。但是,如果发生以下情况,就会终止线程的运行:

  • 线程体中调用了 yield() 方法,让出了对 CPU 的占用权;

  • 线程体中调用了 sleep() 方法,使线程进入睡眠状态;

  • 线程由于 I/O 操作而受阻塞;

  • 另一个更高优先级的线程出现;

  • 在支持时间片的系统中,该线程的时间片用完。

单线程创建方式

单线程创建方式比较简单,一般只有两种方式:继承 Thread 类和实现 Runnable 接口;这两种方式比较常用就不在 Demo 了,但是对于新手需要注意的问题有:

  • 不管是继承 Thread 类还是实现 Runable 接口,业务逻辑是写在 run 方法里面,线程启动的时候是执行 start() 方法;

  • 开启新的线程,不影响主线程的代码执行顺序也不会阻塞主线程的执行;

  • 新的线程和主线程的代码执行顺序是不能够保证先后的;

  • 对于多线程程序,从微观上来讲某一时刻只有一个线程在工作,多线程目的是让 CPU 忙起来;

  • 通过查看 Thread 的源码可以看到,Thread 类是实现了 Runnable 接口的,所以这两种本质上来讲是一个;

PS:平时在工作中也可以借鉴这种代码结构,对上层调用来讲提供更多的选择,作为服务提供方核心业务归一维护

为什么要用线程池

通过上面的介绍,完全可以开发一个多线程的程序,为什么还要引入线程池呢。主要是因为上述单线程方式存在以下几个问题:

  • 线程的工作周期:线程创建所需时间为 T1,线程执行任务所需时间为 T2,线程销毁所需时间为 T3,往往是 T1+T3 大于 T2,所有如果频繁创建线程会损耗过多额外的时间;

  • 如果有任务来了,再去创建线程的话效率比较低,如果从一个池子中可以直接获取可用的线程,那效率会有所提高。所以线程池省去了任务过来,要先创建线程再去执行的过程,节省了时间,提升了效率;

  • 线程池可以管理和控制线程,因为线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控;

  • 线程池提供队列,存放缓冲等待执行的任务。

大致总结了上述的几个原因,所以可以得出一个结论就是在平时工作中,如果要开发多线程程序,尽量要使用线程池的方式来创建和管理线程。

通过线程池创建线程从调用 API 角度来说分为两种,一种是原生的线程池,另外该一种是通过 Java 提供的并发包来创建,后者比较简单,后者其实是对原生的线程池创建方式做了一次简化包装,让调用者使用起来更方便,但道理都是一样的。所以搞明白原生线程池的原理是非常重要的。

ThreadPoolExecutor

通过 ThreadPoolExecutor 创建线程池,API 如下所示:

代码

  1. public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,                          long keepAliveTime,TimeUnit unit,BlockingQueue workQueue);

先来解释下其中的参数含义(如果看的比较模糊可以大致有个印象,后面的图是关键)。

  • corePoolSize

  • 核心池的大小。

在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了 prestartAllCoreThreads() 或者 prestartCoreThread() 方法,从这两个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建 corePoolSize 个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到 corePoolSize 后,就会把到达的任务放到缓存队列当中。

  • maximumPoolSize

线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程。

  • keepAliveTime

表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于 corePoolSize 时,keepAliveTime 才会起作用,直到线程池中的线程数不大于 corePoolSize,即当线程池中的线程数大于 corePoolSize 时,如果一个线程空闲的时间达到 keepAliveTime,则会终止,直到线程池中的线程数不超过 corePoolSize。

但是如果调用了 allowCoreThreadTimeOut(boolean) 方法,在线程池中的线程数不大于 corePoolSize 时,keepAliveTime 参数也会起作用,直到线程池中的线程数为0。

  • unit

参数 keepAliveTime 的时间单位。

  • workQueue

一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下这几种选择:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue。

  • threadFactory

线程工厂,主要用来创建线程。

  • handler

表示当拒绝处理任务时的策略,有以下四种取值:

  • ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出 RejectedExecutionException 异常;

  • ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常;

  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程);

总结

至此,文章终于到了尾声。总结一下,我们谈论了简历制作过程中需要注意的以下三个部分,并分别给出了一些建议:

  1. 技术能力:先写岗位所需能力,再写加分能力,不要写无关能力;
  2. 项目经历:只写明星项目,描述遵循 STAR 法则;
  3. 简历印象:简历遵循三大原则:清晰,简短,必要,要有的放矢,不要海投;

以及最后为大家准备的福利时间:简历模板+Java面试题+热门技术系列教程视频

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

mg-IvVVkb22-1715251316473)]

[外链图片转存中…(img-d05ACIqr-1715251316473)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值