精华内容
参与话题
问答
  • 面试题总结 —— JAVA高级工程师

    万次阅读 多人点赞 2016-03-03 12:03:31
    面试题总结——JAVA高级工程师 近期考虑换工作的问题,于是投简历面试,面试5家公司的高级Java工程师,有4家给了我offer,想着总结一下面试经验,方便最近正在寻求机会的你们 一、无笔试题 不知道是不是职位...

    面试题总结——JAVA高级工程师

    近期考虑换工作的问题,于是投简历面试,面试5家公司的高级Java工程师,有4家给了我offer,想着总结一下面试经验,方便最近正在寻求机会的你们

     

    一、无笔试题

     

    不知道是不是职位原因还是没遇到,面试时,都不需要做笔试题,而是填张个人信息表格,或者直接面试

     

     

    二、三大框架方面问题

     

    1、Spring 事务的隔离性,并说说每个隔离性的区别

    解答:Spring事务详解
     

    2、Spring事务的传播行为,并说说每个传播行为的区别

    解答:Spring事务详解
     

    3、hibernate跟Mybatis/ ibatis 的区别,为什么选择?

    解答:Hibernate与Mybatis的比较
     

    4、Struts跟Spring mvc的优缺点,让你选会如何选

    解答:Spring MVC 与 Struts的区别
     

    5、简单说说Spring 事务机制

    解答:Spring事务机制
     

    6、Spring 4.0新特性

    解答:Spring4新特性

     

     

     

    三、负载均衡、集群相关

    1、weblogic 负载均衡的原理和集群的配置

    解答:a、WEBLOGIC负载均衡原理    b、负载均衡和集群的配置(参考)
     

    2、Nginx+Tomcat+Redis实现负载均衡、资源分离、session共享 

    解答:配置参考
     

    3、nginx配置文件详解——nginx.conf

    解答:Nginx配置文件详细说明

     

     

     

    四、项目优化相关

    1、web如何项目优化

    解答:这个我整理过一次,web项目性能优化(整理)
     

    2、单例模式有几种? 如何优化?

    解答:单例模式的7中用法
     

    3、简单说说线程池的原理和实现

    解答:线程原理及实现

     

     

     

     

     

    五、并发和安全方面

    1、项目并发如何处理?(我们是web项目)

    解答:高并发量网站解决方案,另外,还有数据库乐观锁,数据库读写分离、使用消息队列、多用存储过程等等
     

    2、简单说说功能权限存在的水平权限漏洞和垂直权限漏洞的场景和解决办法(因为我们目前权限级别就是功能权限)

    解答:
    A、水平权限漏洞,如下图




    假设机构有 用户A和用户B 两个用户,其中A有1、2和3权限 ,  用户B有 2 和3 的权限,这时候假设用户B 知道1,并给自己添加1的权限,这时候就是水平权限漏洞。
    目前解决办法:1、限制入口,让用户B无法编辑自己的权限   2、对用户B无法进行向上扩展。最根本的解决办法是深入到数据权限
    解答:水平权限漏洞和解决办法


    B、垂直权限漏洞
    解答:垂直权限漏洞案例和解决方案
     

    3、平台上的图片如何防盗链

    解答:http下载防盗链原理:http协议的字段referer记录来实现
     

    4、如何区分上传的图片是不是木马?

    解答:1、看上传的图片后缀  2、如何后缀是篡改的,那么每个文件有个魔术数字  文件上传-魔术数字
     

    5、消息队列的原理和实现

    解答:1、消息队列原理     2、深入浅出 消息队列 ActiveMQ

     

     

     

     

     

    六、数据库方面

    1、mysql查询字段区不区分大小写?

    解答:不区分,哪怕值也不区分(我当时还反问了,区不区分大小的应用含义有哪些,面试官没说得出来)
     

    2、简单说说数据库集群和负载均衡、分布式(我不懂这块)

    解答:数据库负载均衡和集群参考参考2
     

    3、存储过程的结构和优点

    解答:大概结构  
    存储过程的优缺点
     

    4、触发器的原理和作用

    解答:参考

     

     

     

     

     

     

    大体就这些,还有一些忘了,等记起来再补充,或者其他兄弟姐妹们遇到了,也可以私聊或者回复给我,希望能帮助到大家~  

     

    个人博客:我的博客

     

     

    展开全文
  • Java高级API

    2020-11-03 13:14:40
    Java高级特性涉及面向对象思想的核心内容,在Java体系中发挥着重要作用,设计模式、框架等也是建立在此基础之上。网络编程涉及Socket思想及相关API的使用,是网络应用的基础。输入/输出流是文件操作的基础,也是网络...
  • 面试总结——Java高级工程师(三)

    万次阅读 多人点赞 2016-11-08 23:59:36
    面试前面也总结了一和二, 这第三篇可能更偏向于是内心的独白篇和面试技巧总结吧..... 一、独白 之前也面试别人,现在轮到自己找工作,怎么说呢,每个面试官的看法不一样,面试的方式就不一样,比如我面试别人...

    面试前面也总结了一和二, 这第三篇可能更偏向于是内心的独白篇和面试技巧总结吧.....

     

    一、独白

         之前也面试别人,现在轮到自己找工作,怎么说呢,每个面试官的看法不一样,面试的方式就不一样,比如我面试别人我喜欢问项目中他用到了那些,然后针对用到的技术去问一些问题,或者说对于某些场景的一些技术实现方案是我特别喜欢问的,比如当你的接口服务数据被人截包了,你如何防止数据恶意提交?    相对来说,Java的底层和基础会问的少一点,当然问的少不是代表不问,而是说侧重点在于你的所做过的项目和你的设计思路如何。当然,懂基础和底层更好,这样能让你知其然,更知其所以然,写出来的代码和程序更具有健壮性和可维护性。所以说,基础还是很重要的。

         现在轮到自己出去面试,怎么说呢,其实还是挺紧张的,原以为自己不会因此紧张或者焦虑,实际上,还是有的,在没找到合适的工作的时候,甚至晚上有点睡不着觉,总觉着有什么事压在心头,睡觉都不安心。既然睡不着,那还是看看资料吧,我有个习惯,睡前看点问题,第二天早上就能想到答案,睡前记点资料,第二天早上就能记得特别深刻,不说废话了,直接进入正题吧。

     

     

    二、面试技巧

    1、背熟你的简历

     

    原因:面试的第一个问题,一般都是让你简单介绍下你自己,或者介绍一下你最近的项目,而一个面试者,如果连自己的简历都无法熟知,对里面提到的项目、技术都无法描述清楚的话,我想没有哪家公司会接受这样的,哪怕你是超级人才,你自我表述不行,估计也会为此头疼,所以,切记:一定要背好自己的简历,不要求你能全部记下,至少要熟记你最近所待过的两家公司,这两家公司里面你做过的项目,你负责的模块,项目里面用到的技术以及相对应的技术实现方案(这个尤为重要)。

     

    2、深入了解并熟记部分Java基础知识

    原因:大部分公司无论面试初级还是高级,无论是笔试还是面试,都会问到一系列基础题,这些基础题大概涵括 jvm、字符串、集合、线程等等,如果说每一个让你死记硬背你肯定记不住,那么就是理解中记忆了,拿jvm来说 ,如果让你谈谈你对jvm的理解, 那么你首先得知道JVM有哪些结构组成,每个结构用来做什么的,然后考虑一个Java对象从创建到回收,如何途径JVM这些结构的。如果你从这些方面来综合记忆,相信事半功倍,并且你理解的更透彻。 至于如果让你谈集合,举例List集合下面ArryList、LinkedList、Vector等集合的区别,那么同样的方法,你需要理解每一个的结构组成是什么,你才能知道他有什么作用,这样你在平时的用法上会更精炼,而且在面试过程中,也不至于哑口无言。

     

    3、保持自信心和沉重冷静的心态

    原因:面试过程中,自信是少不了的,相信你可以, 面试的路上可以自己对自己说一句: I belive I can !    反正我就是这么做的,自我的心里暗示吧,其实面对面试官的时候,你自信的状态也会让面试官觉得你是个很有底气的人,至少从感觉上会给你打个高分。另外还有就是保持沉重冷静,如果是让你提供技术方案或者说说某个技术原理,没必要一紧张一咕噜的什么都说,你可以对面试官说:我先想想,然后自己组装记忆中的知识,组装下语言,有条理的说出来,这样能更好的表现你的才能,不是吗?   面试谁都会紧张,我也会紧张,有时候明明记得的知识点忘了,没关系,大胆的说忘了,或者直接说不知道。 要记住,有部分不知道没关系,如果全都知道,那你应该是CTO级别了(开个玩笑)。

     

    4、尽量记住面试过程中你回答不出来或者回答存在不妥的问题

    原因:面试失败了没关系,毕竟每个公司的要求不一样,问的问题和你擅长的方面可能有所出入,但是请记住一点:面试过程中那些你回答不出来问题,或者你自己感觉回答不太准确的问题,自己记下来,如果不会的,你可以当场问问面试官有没有好的解答,如果面试官不愿意告诉你(一般是基础方面面试官就懒得答你),那么你就自己回家慢慢查资料,如果是某些特定的技术,也可以自己写写案例什么的,毕竟知识点就那么多,问题百变,原理不变,面试也是一个学习知识的过程,让你了解大部分公司目前需要或者要求的技术。这次不知道,下次就知道了

     

    5、去面试之前,最好先了解你要去面试公司的情况(包括产品、项目情况)

    原因:俗话说,知己知彼,百战不殆,面试就是一场战斗,你需要了解你面试公司基本情况,包括岗位要求,这样你就能大概知道你需要面试的这家公司的技术要求情况。 为何让你去了解这家公司的主营产品和项目呢,就是让你大概了解这家公司的一个技术架构的情况,包括你可能对他们的一种实现方式提出质疑和疑惑,相信面试官很愿意帮你解答这些问题的。这样你既图了表现,也学到了知识,何乐而不为。

     

    6、合理安排你的面试时间(如果有多家公司的面试机会,尽量把你想去的公司放到最后去面试)

    原因:估计很多人都不理解这个,可能大部分的人对于如何安排面试时间比较迷茫,随意安排。可是这里有个技巧,如果同时有多个面试机会,你把你最想去的公司放到最末尾去面试,这样你经历过了前面的这些公司筛选,如果成功了是个机会,如果没成功,也是为最后做铺垫。  不过这里就需要考虑两点:1、你需要记住你投简历的公司和基本情况(这说明你不是海投的)    2、如果记不住,那么可以先应答一个时间,后续了解公司信息之后,通过邮件或者其他方式与其约定,调整面试时间。而且建议安排一天的面试公司不要超过两家,最好的是上午一家,下午一家,这样你能有充足的时间调整状态。

     

     

    三、面试题基础总结

    1、 JVM结构原理、GC工作机制详解

    答:具体参照:JVM结构、GC工作机制详解     ,说到GC,记住两点:1、GC是负责回收所有无任何引用对象的内存空间。 注意:垃圾回收回收的是无任何引用的对象占据的内存空间而不是对象本身,2、GC回收机制的两种算法,a、引用计数法  b、可达性分析算法(  这里的可达性,大家可以看基础2 Java对象的什么周期),至于更详细的GC算法介绍,大家可以参考:Java GC机制算法

    2、Java对象的生命周期

    答:创建阶段 、 应用阶段 、不可见阶段 、不可达阶段 、收集阶段 、终结阶段、 对象空间重新分配阶段等等,具体参照:Java 对象的生命周期

     

    3、Map或者HashMap的存储原理

    答:HashMap是由数组+链表的一个结构组成,具体参照:HashMap的实现原理

     

    4、当数据表中A、B字段做了组合索引,那么单独使用A或单独使用B会有索引效果吗?(使用like查询如何有索引效果)

    答:看A、B两字段做组合索引的时候,谁在前面,谁在后面,如果A在前,那么单独使用A会有索引效果,单独使用B则没有,反之亦然。同理,使用like模糊查询时,如果只是使用前面%,那么有索引效果,如果使用双%号匹配,那么则无索引效果

     

    5、数据库存储日期格式时,如何考虑时区转换问题?

    答:使用TimeStamp ,  原因参照:Java编程中遇到的时区转换问题

     

    6、Java Object类中有哪些方法?

    答:Object有哪些方法

     

    7、HTTP协议,GET和POST 的区别

    答:浅谈HTTP中GET和POST的区别

     

     

    四、线程、设计模式、缓存方面

    1、SimpleDataFormat是非线程安全的,如何更好的使用而避免风险呢

    答:关于SimpleDateFormat安全的时间格式化线程安全问题

     

    2、如何看待设计模式,并简单说说你对观察者模式的理解

    答:1、设计模式有神马用     2、观察者模式类图及实现

     

    3、集群环境中,session如何实现共享

    答:1、Java集群之session共享    2、session多服务器共享方案,还有一种方案就是使用一个固定的服务器专门保持session,其他服务器共享

     

    4、分布式、集群环境中,缓存如何刷新,如何保持同步?

    答:A、缓存如何刷新? 1、定时刷新  2、主动刷新覆盖   ,每个缓存框架都有自带的刷新机制,或者说缓存失效机制,就拿Redis和 Ehcache举例, 他们都有自带的过期机制,另外主动刷新覆盖时,只需获取对应的key进行数据的覆盖即可

    B、缓存如何保持同步?  这个redis有自带的集群同步机制,即复制功能,具体参考:基于Redis分布式缓存实现      ,Ehcache也有分布式缓存同步的配置,只需要配置不同服务器地址即可,参照:Ehcache分布式缓存同步

     

    5、一条sql执行过长的时间,你如何优化,从哪些方面?

    答:1、查看sql是否涉及多表的联表或者子查询,如果有,看是否能进行业务拆分,相关字段冗余或者合并成临时表(业务和算法的优化)
    2、涉及链表的查询,是否能进行分表查询,单表查询之后的结果进行字段整合
    3、如果以上两种都不能操作,非要链表查询,那么考虑对相对应的查询条件做索引。加快查询速度
    4、针对数量大的表进行历史表分离(如交易流水表)
    5、数据库主从分离,读写分离,降低读写针对同一表同时的压力,至于主从同步,mysql有自带的binlog实现 主从同步

    6、explain分析sql语句,查看执行计划,分析索引是否用上,分析扫描行数等等

    7、查看mysql执行日志,看看是否有其他方面的问题

    个人理解:从根本上来说,查询慢是占用mysql内存比较多,那么可以从这方面去酌手考虑

     

    五、设计方案相关

    面试还会问到一些关于设计方案相关的问题,比如

    1、你的接口服务数据被人截包了,你如何防止数据恶意提交?

    答:我们可以在接口传输参数里面设置一个业务编号,这个编号用来区分是否重复提交。这样即使数据被抓包了,对方也无法区分每个字段你的含义,这时,这个业务编号的作用就来了

     

    2、假设服务器经常宕机,你从哪些方面去排查问题?

    答:这个就留个各位看官补充了,可评论回复

     

     

         面试技巧就补充这些,后面如果记得什么在补充,如果有其他小伙伴有其他建议,也可以在评论回复,其他面试问题还包括算法、数据结构、http协议等等,这些等待大家自己去补充学习了,Spring的原理,Spring mvc的原理问的也挺多的,大家有时间可以看看之前的面试总结——高级JAVA工程(一)和面试总结——高级JAVA工程师(二)   。

    总而言之该看的还是得看,还学的还是得学。再次强调,基础很重要!面试技巧同样很重要,还是那句话:祝愿各位看官都能找到心仪的工作吧~~

        另外,奉劝大家不要频繁跳槽,这些知识点能提升固然好,不要盲目跳槽,找工作很累的,而且没有哪家公司喜欢频繁跳槽的员工

        欢迎加群:157797573

     

     

    展开全文
  • 总结java高级面试题

    万次阅读 多人点赞 2019-05-10 16:25:39
    jvm结构原理,GC工作原理 Jvm结构: Jvm主要包括四个部分: 1、类加载器(ClassLoad) 在JVM启动时或者在类运行时将需要的class加载到JVM中。...类从被加载到虚拟机内存开始,在到卸载出内存为止,正式生命周期...

    jvm结构原理,GC工作原理

    Jvm结构:

                       

    Jvm主要包括四个部分:

    1、类加载器(ClassLoad)

    在JVM启动时或者在类运行时将需要的class加载到JVM中。

     

    类加载时间与过程:

    类从被加载到虚拟机内存开始,在到卸载出内存为止,正式生命周期包括了:加载,验证,准备,解析,初始化,使用和卸载7个阶段。其中验证、准备、解析这个三个步骤被统称为连接(linking)。

                                           

    其中,加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的   ,类的加载过程必须按照这种顺序按部就班的“开始”(仅仅指的是开始,而非执行或者结束,因为这些阶段通常都是互相交叉的混合进行,通常会在一个阶段执行的过程中调用或者激活另一个阶段),而解析阶段则不一定(它在某些情况下可以在初始化阶段之后再开始,这是为了支持java语言的运行时绑定)

     

    在以下几种情况下,会对未初始化的类进行初始化:

    1. 创建类的实例
    2. 对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化
    3. 当初始化一个类的时候,发现其父类没有被初始化,则需要先初始化父类
    4. 当虚拟机启动的时候,用户需要指定一个执行的主类,虚拟机会先初始化这个主类

     

    类实例化和类初始化是两个不同概念:

    类实例化:是指创建一个类的实例对象的过程

    类初始化:是指类中各个类成员(被static修饰的成员变量)赋初始值的过程,是类生命周期的一个过程

     

    ClassLoader的等级加载机制

     

    Java默认提供的三个ClassLoader

    BootStrap ClassLoader:被称为启动类加载机制,是Java类加载层次中最顶层的类加载器,负责加载JDK中核心类库。

    Extension  ClassLoader:被称为扩展类加载器,负责加载Java的扩展类库,Java虚拟机的实现会提供一个扩展目录,该类加载器在此目录里面查找并加载Java类

     

    AppClassLoader:被称为系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件。一版来说,Java应用的类都  是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader()来获取它。

     

    ClassLoader加载类的原理:

               ClassLoader使用的是双亲委托机制来搜索加载类的,每一个ClassLoader实例都有一个父类加载器的引用(不是继承关系,是组合关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但可以用作其它的ClassLoadre实例的父类加载器。当一个ClassLoader实例需要加载某个类时,它会试图亲自搜索某个类之前,先把这个任务委托给它的父类加载器,这个过程是有上之下一次检查的。首先由最顶层的类加载器Bootstrap ClassLoader试图加载,如果没有加载到,则把任务转交给Extension  ClassLoader试图加载,如果没有加载到,则转交给AppClassLoader进行加载,如果它也没有加载到话,则发挥给委托的发起者,有它到指定的文件系统或网络等URL中加载该类,如果都没有加载到此类,那么抛出ClassNotFoundException异常。否则将这个找到的类生成一个类的定义,并将它加载到内存中,最后返回这个类的内存中Class对象。

    类的加载器双亲委托模型:

                                                                   

     

    2、运行时数据区(内存区)

    是在jvm运行的时候操作所分配的内存区。运行时内存区主要分为5个区域:

                                                                       

    方法区(Methd Area):用于存储类结构信息的地方,包括常量池,静态变量,构造函数。虽然JVM规范把方法区描述为堆的一个逻辑部分,但它却有个别名(non-heap 非堆)

                                                           

    Java堆(Heap):存储java实例或者对象的地方。这块是GC的主要区域。从存储内容上可以看到Java堆和方法区是被java线程共享的。

     

    Java栈(Stack):java栈总是和线程关联在一起,每当创建一个线程时,jvm就会为这个线程创建一个对应的java栈。在这个Java栈中又会包含多个帧栈,每运行一个方法就创建一个帧栈,由于存储局部变量,操作栈,方法返回值等。每一个方法从调用直至执行完成的过程,就对应一个帧栈在Java栈中入栈和出栈的过程。所以java栈是私有。

     

    程序计数器(PC Register):用于保存当前线程执行的内存地址。由于JVN程序是多线程执行的(线程轮转切换),所以为了保证线程切换回来,还能回复到原先状态,就需要一个独立的计数器,记录之前中断的地方,可见程序计数器也是线程私有的。

     

    本地方法栈(Native Method Stack):和Java栈的作用差不多,只不过是为JVM使用到的native方法服务的。

     

    3、执行引擎

    负责执行class文件中包含的字节码指令

    4、本地接口

    主要是调用CC++实现的本地方法及返回结果

     

    JVM内存分配:

    Java虚拟机是一次性分配一块较大的内存空间,然后每次new时都在该空间上进行分配和释放,减少了系统调用的次数,节省了一定的开销,这有点类似于内存池的概念。有了这块空间,如何进行分配和回收就跟GC机制有关系了。

    Java一般内存申请有两种:静态内存和动态内存。很容易理解,编译时就能够确定的内存就是静态内存,即内存是固定的,系统一次性分配。比如int类型变量;动态内存分配就是在程序执行时才知道要分配的存储空间大小,比如java对象的内存空间。综上所述:java栈、程序计数器、本地方法栈都是线程私有的,线程生就生,线程灭就灭,栈中的栈帧随着方法的结束也会撤销,内存自然就跟着回收了。所以几个地方的内存分配和回收是确定的,不需要管。但是java堆和方法区则不一样,我们只有在程序运行期间才知道会创建哪些对象,所以这部分内存分配和回收是动态的。一般说的垃圾回收也是针对这部分的。

     

    垃圾检测和回收算法

             垃圾收集器一般必须完成两件事:检测出垃圾,回收垃圾。检测垃圾一般分为以下几种:

     

    引用计数法:给对象增加一个引用计数器,每当有地方引用这个对象时,就在计数器上加1,引用失效就减1

    可达性分析算法:以根集对象为起始点进行搜索,如果有对象不可达的话,即是垃圾对象。这里的根集一般包括java堆中引用的对象,方法区常量池的引用对象。

     

    总之,jvm在做垃圾回收的时候,会检查堆中的所有对象是否会被这些根集对象引用,不能够被引用的对象就会被垃圾收集器回收。一般回收有一下几种方法:

             1、标记-清除(Mark-sweep):分为两个阶段,标记和清楚。标记所有需要回收的对象,然后统一回收这个是最基础的算法,后续的收集算法 都是基于这个算法扩展的。

             不足:效率低;标记清楚之后会产生大量的碎片。

             2、复制(copying): 此算法把内存空间划分为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前区域,把正在使用中的对象复制到另外区域中。此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去还能进行相应的内存整理,不会出现“内存碎片”问题。当然,此算法的缺点也是很明显,就是需要双倍内存。

             3、标记-整理(Mark-Compact):此算法结合了“标记-清除”和“复制”两个算法的有点。也是两个阶段,第一阶段从根节点开始标记所被引用的对象。第二阶段遍历整个堆,把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“复制”算法的空间问题

             4、分代收集算法:这是当前商业虚拟机常用的垃圾收集算法。分代的垃圾回收策率,是基于这样一个事实:不同的对象的生命周期不一样的。因此,不同生命周期的对象采取不同的收集方式,以便提高回收效率。

            

    为什么要运用分代垃圾回收策率?在java程序运行的过程中,会产生大量的对象,因每个对象所能承担的职责和功能不同,所以也有着不同的生命周期。有的对象生命周期较长,有的对象生命周期较短。试想,在不进行对象存活时间区分的情况下,每次垃圾回收都是对整个堆空间进行回收,那么消耗的时间相对比较长,而对于存活时间较长的对象进行扫描的工作是徒劳的。因此就引入了分治思想,所以分治思想就是因地制宜,将对象进行代划分,把不同的生命周期的对象放在不同的代上使用不同的垃圾回收方法。

     

    如果划分?将对象按其生命周期的不同划分成:年轻代(Young Generation)、老年代(Old Generation)、持久代(Permanent Generation)。其中持久代主要存放的是类信息,所以与java对象的回收关系不大,与回收信息相关的是年轻代,老年代。

     

    年轻代:是所有新对象产生的地方。年轻代被分为3个部分:Ender(出生)区和两个Survivor(幸存者)区(From和To)。当Ender区被对象填满时,就会执行Minor GC。并把所有存活下来的对象转移到其中一个Survivor区(假设为from区)。Minor GC同样会检查存活下来的对象,并把他们转移到另一个Survivor区(假设为to区)。这样在一段时间内总会有一个空的Survivor区。经过多次GC后,仍然存活下来的对象会被转移到老年代内存空间。

     

    年老带:在年轻代经过N次回收的仍然没有被清除的对象会放到老年代,可以说它们是久经沙场而不亡的一代,都是生命周期较长的对象。对于老年代和永久代,就不能再采用年轻代中那样搬移腾挪的回收算法,因为那些对于这些回收战场上的老兵来说是小儿科,通常会在老年代内存被占满时将会触发Full GC,回收整个堆内存。

     

    持久代:用于存放静态文件,比如Java类,方法等。持久代对垃圾回收没有显著的影响。

                                                                             

    我这里之所以最后讲分代,是因为分代里涉及了前面几种算法。年轻代:涉及了复制算法;年老代:涉及了标记-整理(Mark-Sweep的算法。

     

    Java线程池

    java中的ThreadPoolExecutor类

    ThreadPoolExecutor类继承了AbstractExecutorService类,并提供了四个构造器。构造器中分别有一下参数:

    1. corePoolSize:核心池的大小,在创建线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法来预创建线程。即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
    2. maximumPoolSize:线程池最大的线程数,表示在线程池中最多能创建多少个线程;
    3. keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize。即当线程池中的线程数大于corePoolSize时,如果一个线程空闲时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,知道线程池中的线程数为0。
    4. unit:参数keepAliveTime的时间单位,有7中取值,在TimeUnit类中有7中静态属性:
      1. TimeUnit.DAYS;               //天
      2. TimeUnit.HOURS;             //小时
      3. TimeUnit.MINUTES;           //分钟
      4. TimeUnit.SECONDS;           //秒
      5. TimeUnit.MILLISECONDS;      //毫秒
      6. TimeUnit.MICROSECONDS;      //微妙
      7. TimeUnit.NANOSECONDS;       //纳秒
    5. workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一版来说阻塞队列有以下几种:
      1. ArrayBlockingQueue
      2. LinkedBlockingQueue
      3. SynchronousQueue

    ArrayBlockingQueue使用较少,一使用LinkedBlokingQueue和SynchronousQueue。线程池排队策率与BlockingQueue有关

    1. threadFactory:线程工厂,主要用来创建线程;
    2. hander:表示当拒绝处理任务时的策率,有一下四种取值:
      1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
      2. ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常
      3. ThreadPoolExecutor.DiscardOlddestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
      4. ThreadPoolExecutor.CallerRunsPolicy:有调用线程处理该任务

     

    ThreadPoolExecutor继承了AbstractExecutorService抽象类,AbstractExecutorService抽象类实现了ExecutorService接口,ExecutorService接口继承了Executor接口。

     

    Executor:是一个顶层接口,在它里面只有一个方法execute(Runnable),返回值为void,参数为Runnable类型,其实就是为了执行传进来的任务;

    然后ExecutorService接口继承了Executor接口,并生命了一些方法:submit、invokeAll、invokeAny以及shutDown等。

    抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService接口中所有的方法;

    然后ThreadPoolExecutor继承了抽象类AbstractExecutorService;

    在ThreadPoolExecutor类中有几个非常重要的方法:

    1. execute():实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交线程池去执行。
    2. submit():此方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和executor()方法不同,它能够返回任务执行的结果,去看submit()方法的方法实现,会发现它实际上还是调用的execute()方法,只不过它利用了Futrure来获取任务的执行结果
    3. shutDown():是用来关闭线程
    4. shutDownNow():是用来关闭所有线程

     

    深入剖析线程池实现的原理图

    深入解析一下线程池的具体实现原理,将从下面几个方面讲解:

    1. 线程池状态

    在ThreadPoolExecutor中定义了一个volatile变量,另外定义了几个static final变量表示线程池的各个状态:

    volatile in runSize;

    static final int Running = 0;

    static final int shutdown = 1;

    static final int stop = 2;

    static final int terminated=3;

     

    runSize:表示当前线程池状态,它是一个volatile变量用来保证线程之间的可见性

    下面几个static final int 变量表示runSize可能的几个取值

    当创建线程池后,初始时,线程池处于Running状态;

    如果调用了shutdown()方法,则线程池处于shutdown状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;

    如果滴啊用shutdownNow()方法,则线程池处于stop状态,此时线程池不能接受新的任务,并且会尝试终止正在执行的任务;

    当线程池处于shutdown或者stop状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为terminated状态

     

    1. 任务的执行

    ThreadPoolExecutor类中其他的一些比较重要的成员变量:

    private final BlockingQueue<Runnable> workQueue; //任务缓存队列,用来存放等待执行的任务

    private final ReentrantLock mainLock = new ReentrantLock();//线程池的主要状态锁,对线程池状态(比如线程池大小等)的改变都要使用这个锁

    private final HashSet<Worker> workers = new HashSet<Worker>(); //用来存放工作集

    private volatile long  keepAliveTime;    //线程存货时间

    private volatile boolean allowCoreThreadTimeOut;   //是否允许为核心线程设置存活时间

    private volatile int   corePoolSize;   //核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)

    private volatile int   maximumPoolSize;   //线程池最大能容忍的线程数

    private volatile int   poolSize;       //线程池中当前的线程数

    private volatile RejectedExecutionHandler handler; //任务拒绝策略

    private volatile ThreadFactory threadFactory;   //线程工厂,用来创建线程

    private int largestPoolSize;   //用来记录线程池中曾经出现过的最大线程数

    private long completedTaskCount;   //用来记录已经执行完毕的任务个数

     

    任务提交给线程池之后处理的策略,主要有一下四点:

    如果当前线程池中线程数目小于corePoolSize,则每来一个线程,就会创建一个线程去执行这个任务;

    如果当前线程池中线程数据大于等于corePoolSize,则没来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一版来说是任务缓存队列已满),则会尝试尝试创建新的线程去执行这个任务;

    如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策率进行处理;

    如果线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数据不大于corePoolSize;如果允许核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止

     

    1. 线程池中的线程初始化

    默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程。在实际中如果需要线程池创建之后立即创建线程,可以通过下面两种方法:

    prestartCoreThread():初始化一个核心线程

    prestartAllCoreThread():初始化所有核心线程

     

    1. 任务缓存队列及排队策略

    任务缓存队列,即workQueue,它用来存放等待执行的任务

    workQueue的类型为BlockingQueue<Runnable>,通常可以取下面三种类型:

    ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须制定大小

    LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列的大小,那么默认为Integer.MAX_VALUE

    synchronousQueue:这个队列比较特殊,他不会保存提交的任务,而是将直接新建的一个线程来执行新的任务。

    1. 任务拒绝策略

    当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常通过一下四种策略:

    1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
    2. ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常
    3. ThreadPoolExecutor.DiscardOlddestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    4. ThreadPoolExecutor.CallerRunsPolicy:有调用线程处理该任务

     

    1. 线程池的关闭

    ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别为shutDown()和shutDownNow(),其中:

    shutDown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会执行新的任务

    shutDownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

    1. 线程池容量的动态调动

    ThreadPoolExecutor提供了动态调用线程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),

    setCorePool():设置核心池大小

    setMaximumPoolSize():设置线程池最大能创建的线程数目大小

    当上述参数从小变大时,ThreadPoolExecutor进行线程赋值,还可能立即创建新的线程来执行任务

    使用示例

    package ThreadPoolExecutor;

     

    public class MyTask implements Runnable {

        private int taskNum;

       

        public MyTask(int num) {

            this.taskNum = num;

        }

     

        @Override

        public void run() {

             System.out.println("正在执行task"+taskNum);

             try {

                Thread.currentThread().sleep(4000);

            } catch (InterruptedException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

             System.out.println("task" + taskNum + "执行完毕");

        }

     

    }

     

    package ThreadPoolExecutor;

     

    import java.util.concurrent.ArrayBlockingQueue;

    import java.util.concurrent.ThreadPoolExecutor;

    import java.util.concurrent.TimeUnit;

     

    public class Test {

        public static void main(String[] args) {

            ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,

                    new ArrayBlockingQueue<Runnable>(5));

            for(int i = 0 ; i < 15 ; i ++){

                MyTask mytask = new MyTask(i);

                executor.execute(mytask);

                 System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+

                         executor.getQueue().size()+",已执行玩别的任务数目:"+executor.getCompletedTaskCount());

            }

            executor.shutdown();

        }

    }

    执行结果:

    正在执行task0

    线程池中线程数目:1,队列中等待执行的任务数目:0,已执行玩别的任务数目:0

    线程池中线程数目:2,队列中等待执行的任务数目:0,已执行玩别的任务数目:0

    正在执行task1

    线程池中线程数目:3,队列中等待执行的任务数目:0,已执行玩别的任务数目:0

    正在执行task2

    线程池中线程数目:4,队列中等待执行的任务数目:0,已执行玩别的任务数目:0

    正在执行task3

    线程池中线程数目:5,队列中等待执行的任务数目:0,已执行玩别的任务数目:0

    正在执行task4

    线程池中线程数目:5,队列中等待执行的任务数目:1,已执行玩别的任务数目:0

    线程池中线程数目:5,队列中等待执行的任务数目:2,已执行玩别的任务数目:0

    线程池中线程数目:5,队列中等待执行的任务数目:3,已执行玩别的任务数目:0

    线程池中线程数目:5,队列中等待执行的任务数目:4,已执行玩别的任务数目:0

    线程池中线程数目:5,队列中等待执行的任务数目:5,已执行玩别的任务数目:0

    线程池中线程数目:6,队列中等待执行的任务数目:5,已执行玩别的任务数目:0

    正在执行task10

    正在执行task11

    线程池中线程数目:7,队列中等待执行的任务数目:5,已执行玩别的任务数目:0

    线程池中线程数目:8,队列中等待执行的任务数目:5,已执行玩别的任务数目:0

    线程池中线程数目:9,队列中等待执行的任务数目:5,已执行玩别的任务数目:0

    正在执行task13

    正在执行task12

    线程池中线程数目:10,队列中等待执行的任务数目:5,已执行玩别的任务数目:0

    正在执行task14

    task0执行完毕

    正在执行task5

    task1执行完毕

    正在执行task6

    task2执行完毕

    正在执行task7

    task3执行完毕

    正在执行task8

    task4执行完毕

    正在执行task9

    task11执行完毕

    task10执行完毕

    task13执行完毕

    task14执行完毕

    task12执行完毕

    task5执行完毕

    task6执行完毕

    task9执行完毕

    task8执行完毕

    task7执行完毕

     

    从执行结果可以看出,当线程池中的线程的数目大于5时,便将任务放入缓存队列里面,当任务缓存队列满了之后,便创建新的线程。如果上面程序中,将for循环中改成执行20个任务,就会抛出任务拒绝异常了;

     

    在java doc中,并不建议直接使用ThreadPoolExecutor直接创建线程池,而是使用Executor类中提供的几个静态方法来创建线程池:

    Executor.newCachedThread()://创建一个缓冲池,缓冲池大小为Integer.MAX_VALUE

    Executor.newSingleThreadExecutor()://创建容量为1的缓冲池

    Executor.newFixedThreadPool()://创建固定容量的缓冲池

     

    以下是这个三个静态方法的具体实现:

    public static ExecutorService newFixedThreadPool(int nThreads) {

        return new ThreadPoolExecutor(nThreads, nThreads,

                                      0L, TimeUnit.MILLISECONDS,

                                      new LinkedBlockingQueue<Runnable>());

    }

    public static ExecutorService newSingleThreadExecutor() {

        return new FinalizableDelegatedExecutorService

            (new ThreadPoolExecutor(1, 1,

                                    0L, TimeUnit.MILLISECONDS,

                                    new LinkedBlockingQueue<Runnable>()));

    }

    public static ExecutorService newCachedThreadPool() {

        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

                                      60L, TimeUnit.SECONDS,

                                      new SynchronousQueue<Runnable>());

    }

     

    从它们的具体实现来看,他们实际上也是调用了ThreadPoolExecutor,只不过参数都已经配置好了,

     

    newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;

    newSingleThreadPool将corePoolSize和maximumPoolSize的值都设置为1,也使用的LinkedBlockingQueue;

    newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程

     

    实际工作中,如果Executors提供的三种静态方法如果能够满足要求,就尽量使用它提供的三个方法,因为自己手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数据量来进行配置。如果ThreadPoolExecutor达不到要求,可以自己继承ThreadPoolExecutor类进行重写

     

    如何合理配置线程池的大小

    一般需要根据任务的类型来配置线程池大小;

    如果是CPU密集型任务,就需要压榨CPU,参考值可以设为Ncpu+1

    如果是IO密集型任务,参考值可以设为2*Ncpu

    当然,这只是一个参考值,具体的设置还需要根据实际情况进行调整,比如可以先将线程池大小设置为参考值,再观察任务运行情况和系统负载、资源利用率来进行适当调整。

     

    Java四种线程池

    线程池的优势:

    1. 重用存在的线程,减少对象的创建,消亡的内存开销,性能佳
    2. 可有效控制最大并发线程数,提高系统资源使用率,同时避免过多资源竞争,避免堵塞
    3. 提供定时执行,定期执行,单线程,并发数控制功能。

    Executors提供的四种线程池:

    1. newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需求,可灵活回收空线程,若无可回收的线程,则新建线程

    package ThreadPoolExecutor;

     

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

     

    public class CachedThreadPool {

             public static void main(String[] args) {

                       ExecutorService cachedThreadPool = Executors.newCachedThreadPool();            

                       for(int i = 0 ; i < 10 ; i++ ){

                                final int index = 1;

                                try {

                                         Thread.sleep(index * 1000);

                                } catch (InterruptedException e) {

                                         // TODO Auto-generated catch block

                                         e.printStackTrace();

                                }                          

                                cachedThreadPool.execute(new Runnable(){

                                         @Override

                                         public void run() {

                                                   System.out.println(index);

                                         }                                   

                                });

                       }

                       cachedThreadPool.shutdown();//立即终止线程池

             }

    }

    线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不是每次新增线程

     

    1. newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

    package ThreadPoolExecutor;

     

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

     

    public class FixedThreadPool {

             public static void main(String[] args) {

                       ExecutorService fixedThreadPool  = Executors.newFixedThreadPool(3);

                       for(int i = 0 ; i < 10 ; i ++){

                                final int index = 1;

                                fixedThreadPool.execute(new Runnable(){

                                         @Override

                                         public void run() {

                                                   System.out.println(index);

                                                   try {

                                                            Thread.sleep(2000);

                                                   } catch (InterruptedException e) {

                                                            // TODO Auto-generated catch block

                                                            e.printStackTrace();

                                                   }

                                         }

                                });

                       }       

    fixedThreadPool.shutdown();

             }

    }

    因为线程池大小为3,每个任务输出index后sleep2秒,所以每两秒打印三个数字;

     

    1. newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行

    package ThreadPoolExecutor;

     

    import java.util.concurrent.Executors;

    import java.util.concurrent.ScheduledExecutorService;

    import java.util.concurrent.TimeUnit;

     

    public class ScheduledThreadPool {

             public static void main(String[] args) {

                       ScheduledExecutorService  scheduledThreadPool = Executors.newScheduledThreadPool(5);

                       scheduledThreadPool.schedule(new Runnable(){

                                @Override

                                public void run() {

                                         System.out.println("这是一个延迟线程");

                                }

                               

                       }, 3, TimeUnit.SECONDS);// 表示延迟3秒执行

                      

                       scheduledThreadPool.scheduleAtFixedRate(new Runnable(){

                                @Override

                                public void run() {

                                         System.out.println("delay 1 seconds, and excute every 3 seconds");

                                }

                               

                       }, 1, 2, TimeUnit.SECONDS);// 表示延迟1秒后每3秒执行一次

                      

                       //scheduledThreadPool.shutdown();

             }

    }

     

    1. newSingleThreadPool:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定的顺序(FIFO,LIFO)执行

     

    package ThreadPoolExecutor;

     

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

     

    public class singleThreadExecutor {

      public static void main(String[] args) {

          ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

          for(int i = 0 ; i < 10; i++){

              final int index = 1;

              singleThreadExecutor.execute(new Runnable(){

     

                  @Override

                  public void run() {

                      System.out.println(index);

                      try {

                          Thread.sleep(2000);

                      } catch (InterruptedException e) {

                          // TODO Auto-generated catch block

                          e.printStackTrace();

                      }

                  }

                 

              });

          }

          singleThreadExecutor.shutdown();

      }

    }

    结果依次输出,相当于顺序执行各个任务

     

    线程池的作用:

    线程池是为了限制系统中执行线程的数量

             根据系统环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其它线程排队等候。一个任务执行完毕,再从队列中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。

             为什么要用线程池:

    1. 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务
    2. 可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,增加服务器的压力(每个线程大约需要1MB的内存,线程开的越多,消耗内存越大,最后导致司机);

    Java里面线程池的顶级接口是Executor,但是严格意义上将Executor并不是一个线程池,而是一个执行线程的工具。真正的线程池接口是ExecutorService.

             比较重要的几个类:

             Executor:真正的线程池接口

             ScheduledExecutorService:能和Timer/TimerTask类似,解决那些需要任务重复执行的问题

             ThreadPoolService:ExecutorService默认实现类

             ScheduledExecutorPoolExecutor:继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

    要配置一个线程池是复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是最优的,因此在Executors类里面提供了一个静态工厂,生成一些常用的线程池。

     

    1. newSingleThreadExecutor

    创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程在串行执行所有的任务。如果这个唯一的线程因为异常信息中断,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

    1. newFixedThreadPool

    创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程

    1. newCachedThreadPool

    创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么回收空闲(60秒不执行)的线程,当任务增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

    1. newScheduledThreadPool

    创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

     

    一个任务通过execute(Runnable)方法被添加到线程池,任务就是一个Runnable类型的对象,任务的执行方法就是Runnable类型对象的run()方法

    当一个任务通过execute(Runnable)方法欲添加到线程池时:

    如果此时线程池中的数据小于corePoolSize,即使线程池中的线程处于空闲状态,也要创建新的线程来处理被添加的任务。

    如果此时线程池中的数据等于 corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列

    如果此时线程池中的数据大于 corePoolSize,缓冲队列workQueue已满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。

    如果此时线程池中的数量大于 corePoolSize,缓冲队列workQueue已满,并且线程池中的数量等于maximumPoolSize,那么通过handler所指定的策略来处理此任务。

     

    处理任务的优先级:

    核心线程corePoolSize,任务队列workQueue,最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • java高级面试题2019

    千次阅读 多人点赞 2019-05-16 09:36:25
    2、Java程序怎么优化? 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串 4、 说说http,https协议 5、请写一段栈溢出、堆溢出的代码 6、Java集合面试题及答案总结 7、Java代码优化...

    1、HashMap实现原理
    2、Java程序怎么优化?
    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串
    4、 说说http,https协议
    5、请写一段栈溢出、堆溢出的代码
    6、Java集合面试题及答案总结
    7、Java代码优化(上)
    8、Java代码优化(下)
    9、史上最有用的java面试题整理
    10、线程池工作原理,任务拒接策略有哪几种
    11、虚拟机JVM 组成部分
    12、JAVA性能优化
    13、web应用安全问题
    14、并发问题
    15、synchronized和lock区别,可重入锁与非可重入锁的区别
    16、aop代理模式
    17、jdk1.8新特性
    18、java的4种引用 强软弱虚
    19、类加载过程
    20、模板方法模式
    21、开闭原则
    22、CAS机制是什么?有什么缺点,会出现什么问题
    23、分布式理论(CAP,Base,paxos)
    24、Arrays.sort 实现原理和 Collection 实现原理
    25、synchronized 的实现原理以及锁优化
    26、volatile 的实现原理
    27、Java 的信号灯
    28、synchronized 在静态方法和普通方法的区别?
    29、ConcurrenHashMap介绍1.8 中为什么要用红黑树
    30、如何检测死锁?怎么预防死锁
    31、线程池的种类,区别和使用场景
    32、分析线程池的实现原理和线程的调度过程
    33、 ReenTrantLock可重入锁(和synchronized的区别)总结
    34、堆内存溢出及解决方法
    35、你了解大O符号(big-O notation)么?你能给出不同数据结构的例子么?
    36、什么是Java优先级队列(Priority Queue)?
    37、15个Java线程并发面试题和答案
    38、什么是 CopyOnWriteArrayList,它与 ArrayList 有何不同?
    39、说说你对设计模式的看法
    40、开发中都用到了那些设计模式?用在什么场合?
    41、设计模式是什么,设计模式有什么作用?
    42、23种经典设计模式都有哪些,如何分类?
    43、数据库连接池的原理是什么
    44、简述Java内存管理机制,以及垃圾回收的原理和使用过Java调优工具
    45、GC线程是否为守护线程?
    46、Java的类加载器都有哪些,每个类加载器都有加载那些类,什么是双亲委派模型,是做什么的?
    47、java二叉树算法面试题大全含答案
    转自java面试题网

    展开全文
  • 面试总结——Java高级工程师(二)

    万次阅读 多人点赞 2016-10-25 23:52:50
    时运不济,才到16年10月份,公司资金紧张,不得已又...一、Java底层基础题 1、SpringMVC的原理以及返回数据如何渲染到jsp/html上? 答:Spring MVC的核心就是DispatcherServlet , 一个请求经过DispatcherServlet...
  • Java高级工程师常见面试题-总结

    千次阅读 多人点赞 2018-07-23 10:03:43
    Java高级工程师常见面试题(一)-Java基础 》 《Java高级工程师常见面试题(二)-Java IO》 《Java高级工程师常见面试题(三)-Java Web》 《Java高级工程师常见面试题(四)-JVM》 《Java高级工程师常见面试...
  • https://tieba.baidu.com/p/6626343323?pid=131596903736&cid=0&red_tag=2381637719#131596903736
  • Java高级技术

    千人学习 2018-12-19 18:46:50
    本阶段课程涵盖Java开发流行的自动化构建工具:Maven,版本控制系统:SVN和Git,容器虚拟化技术:Docker,权限模型:RBAC,集成测试:Jenkins,微服务架构:SpringCloud等核心内容。旨在应对各种实际开发情况下的的各种...
  • 阿里巴巴2020首发136道Java高级岗面试题(含答案)

    万次阅读 多人点赞 2020-07-10 15:03:49
    整理的136道阿里的Java面试题,都来挑战一下,看看自己有多厉害。下面题目都带超详细的解答,详情见底部。 java基础 Arrays.sort实现原理和Collection实现原理 foreach和while的区别(编译之后) 线程池的种类,...
  • Java高级开发工程师】近一个月的面试总结

    万次阅读 多人点赞 2017-06-22 19:52:18
    时隔两年,再一次的面临离职找工作,这一次换工作有些许的不舍,也有些许的无奈。个人所在的技术团队不错,两年时间成长了很多,也很不舍这个团队。但是,由于公司的某些原因和对于自身未来发展的综合考虑,又不得...
  • JAVA高级——集合类

    万次阅读 2016-12-13 13:47:11
    集合的概念:Java中集合类是用来存放对象的集合相当于一个容器,里面包容着一组对象 —— 容器类其中的每个对象作为集合的一个元素出现Java API提供的集合类位于java.util包内Java中数组与集合的比较:数组也是容器...
  • 阿里java高级工程师面试100题

    万次阅读 多人点赞 2017-08-10 21:22:25
    1,java堆,分新生代老年代,新生代有Eden,from surviver,to surviver三个空间,堆被所有线程共。eden内存不足时,发生一次minor GC,会把from survivor和eden的对象复制到to survivor,这次的to survivor就变成...
  • Java高级技术第三章——Java核心类库

    千次阅读 2018-02-12 10:24:47
    Java自带很多实用的包,这些包中定义了很多类库,可以很方便地进行代码重用。 常用的Java核心包及其功能是: 包名 主要功能 java.applet 提供了创建applet需要的所有类 java.awt.* ...
  • Java高级swing编程,怎样去掉jframe外边框。Java高级swing编程,
  • Java高级开发工程师面试笔记

    千次阅读 2018-01-22 10:04:05
    最近在复习面试相关的知识点,然后做笔记,后期(大概在2018.02.01)会分享给大家,尽自己最大的努力做到最好,还希望到时候大家能给予建议和补充 ----------------2018.03.05------------------- ...
  • 我认为的Java高级篇内容是这样划分的:对Java这个编程语言有基本了解,基本掌握了基础语法,能够利用基础部分知识去写一些小项目,例如,我们的webUI自动化测试包括简单的框架设计。而Java的高级部分,主要从基础...
  • Java学习之java高级特性

    万次阅读 多人点赞 2018-04-13 11:11:30
    下面是个人的总结 一、集合框架及泛型1、集合框架是一套性能优良、使用方便的接口和类(位于java.util包中)解决数组在存储上不能很好适应元素数量动态变化,查找效率低的缺陷集合接口: Map、Collection(子接口List...
  • java高级开发工程师简历大全,都是筛选出来的非常不错的简历。
  • Java 高级教程

    千次阅读 2018-03-13 13:18:27
    //转载自菜鸟教程Java 数据结构Java工具包提供了强大的数据结构。在Java中的数据结构主要包括以下几种接口和类:枚举(Enumeration)位集合(BitSet)向量(Vector)栈(Stack)字典(Dictionary)哈希表(Hashtable...
  • Java高级编程

    千次阅读 2018-01-15 15:43:03
    Java泛型 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。 泛型的本质是参数化类型,也就是说所操作的数据类型被指定...
  • Java高级特性之解析XML

    千次阅读 2016-06-14 20:12:18
    XML与Java的渊源: 我们来看XML是网络传输的一种数据规范,或者也叫可扩展的标记语言。可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 XML使用DTD(document type definition)...
  • Java高级技术系列之前言

    千次阅读 2018-01-20 11:51:48
    Java高级技术系列之前言 写的是什么?   Java高级技术系列主要写有关Java编程中的一些高级特性,难点和技巧性内容,算是对即有知识的一个总结与梳理。大致内容从比较简单的反射,注解,高并发,设计模式等,再到...
  • Java学习视频教程 云析学院Java高级架构实战系列 Java培训机构都会录制比较完整的入门级视频教程提供下载的,单是想要入门的话下载看这些云析学院Java就够了,如果要深入的学习可以参考他们列出来的教学提纲去网络...
  • JAVA高级面试题——2019

    万次阅读 多人点赞 2019-07-13 16:28:02
    HashMap是如何存储的? HashMap指针碰撞原理 Redis如何做高可用? Redis如何实现队列? Redis如何做持久化? Redis做持久化为什么会降低性能? Redis哨兵机制? RabbitMq如何做高可用?...Mysql如何做高可...
  • 怎样获取本地时间,怎样获取这周星期六是几号。Java高级应用指南
  • Java高级部分流重点总结上

    千次阅读 2016-01-10 21:07:52
    Java高级部分流重点总结上 作者:数据分析玩家 1、简述Java中流的重要意义 Java中流的引入,打破了传统意义上输入设备就是键盘,输出设备就是显示器的这种局限性,用户可以自行设计输入设备与输出设备,从而进行...
  • Java高级技术梳理

    千次阅读 2019-08-05 12:19:17
    学习完java高级阶段想对所学习到的知识进行梳理,借此回顾自己所学习到的知识 在这一阶段,首先是安装了虚拟机. 在安装虚拟机过后,学习了以后,就开始安装了jdk, tomcat, mysql . 在安装好这些环境后,我们算是吧...
  • Java高级篇整理

    万次阅读 多人点赞 2017-07-26 19:50:47
    以博客的形式梳理思路,通过一行行代码,深入理解Java细节,从而对于Java高级特性进行全面掌握。 Java三大特性 封装 封装三大好处  良好的封装减少耦合类内部的结构可以自由修改(无须修改客户代码)...

空空如也

1 2 3 4 5 ... 20
收藏数 56,823
精华内容 22,729
关键字:

java高级

java 订阅