面试题
[TOC]
JDK1.8的新特性
- Lambda表达式以及函数式编程
- 接口的默认方法以及静态方法
- Optional 和 Stream
- 日期时间:LocalDate(保存日期部分,有时区信息),LocalTime(不包含时区信息)
- Nashorn Javascript引擎
- 并行Sort ParallelSort
G1垃圾回收和其他的区别
-
串行回收:主要面向单线程环境
-
并行/吞吐量回收器:JVM默认回收器,Parallel collector,最大的有点是使用多个线程来扫描以及压缩对,其缺点是minorGC或者fullGC都会暂停应用程序
-
CMS回收器:这个算法使用了多个线程来扫描对并标记那些不再使用的可以回收的对象。这个算法在两种情况下会进入一个“stop the world”的模式。但进行跟对象的·初始标记的时候(老年代中线程入口点或者静态变量科大的哪些对象)以及当这个算法在并发运行的时候应用程序改变了对的状态是的他不得不得回去确认自己的标记是或否都是正确的。还有一个使用这个垃圾回收器碰到的问题是promotion fail,在这时候老年代没有多余的空间,他只能进行一次stw的full gc,为了去表不会发生这种事情,要么增加的老年代的大小。
还有一个缺点就是和并行回收器相比,他使用的CPU资源会跟多,假如你的堆小于4G,而你又希望分配更多的CPU资源以避免应用暂停。
-
G1回收器:G1回收器将堆分为多个区域,大小从1MB到32MB不等,并使用多个后台线程来扫描他们。G1回收器会优先扫描那些包含垃圾最多的区域,这正是他名字的由来。
这一策略减少了后台线程还未扫描完无用对象前堆就已经用完的可能性,那种情况回收器就必须暂停应用,这会导致STW回收。G1的另一个好处就是他总会进行堆的压缩,二CMS回收器只用在Full GC的时候才会干这个事。
现在的微服务包括格力程序的组件,简化部署,便面重新加载应用类到内存所长城的开销
这么做主要还是避免中长期的“stop the world”的暂停
G1回收器的字符串去重,由于字符串占用了大多数的对空间,这样的优化是的G1回收器能识别出对重的那些重复出现的字符串并将他们指向同一个内部的char[]数组,以避免一个字符串的多份拷贝,这样的对的使用效率会变得很低。
-
JAVA 8的最大的改变就是持久带的移除,本来是给类元数据,主流字符串,静态变量来分开空间,本来需要开发人院针对那些回家再大量累的应用专门进行对比例的优化调整
哪些内存需要回收
通过根搜索法,经过一次标记之后,任然没有复活的对象。
哪些对象可以成为根,虚拟机栈中引用的对象、方法去中今天静态应用对象、方法中常量应用的对象、本地犯法栈JNI的引用对象。
引用计数法无法解决相互依赖的问题。
何时回收
在eden区满,无法为新生代对象分配内存的时候触发minor(少量的) gc;进去老年代的对象无法分配足够的内存空间 触发full gc;
如何回收
新生代对象,使用复制的算法
老年代,标记--清除算法
spring IOC和AOP的原理
将对象交给容器管理,你只需要在spring配置文件中配置对应的bean以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类(假设这个类名是A),分配的方法就是调用A的setter方法来注入,而不需要你在A里面new这些bean了。
AOP可以说是对OOP的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
Java中的HashMap的工作原理是什么
ava中的HashMap是以键值对(key-value)的形式存储元素的。HashMap需要一个hash函数,它使用hashCode()和equals()方法来向集合/从集合添加和检索元素。当调用put()方法的时候,HashMap会计算key的hash值,然后把键值对存储在集合中合适的索引上。如果key已经存在了,value会被更新成新值。HashMap的一些重要的特性是它的容量(capacity),负载因子(load factor)和扩容极限(threshold resizing)。
hashCode()和equals()方法的重要性体现在什么地方
Java中的HashMap使用hashCode()和equals()方法来确定键值对的索引,当根据键获取值的时候也会用到这两个方法。如果没有正确的实现这两个方法,两个不同的键可能会有相同的hash值,因此,可能会被集合认为是相等的。而且,这两个方法也用来发现重复元素。所以这两个方法的实现对HashMap的精确性和正确性是至关重要的
HashMap和Hashtable有什么区别
HashMap和Hashtable都实现了Map接口,因此很多特性非常相似。但是,他们有以下不同点:
HashMap允许键和值是null,而Hashtable不允许键或者值是null。
Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
HashMap提供了可供应用迭代的键的集合,因此,HashMap是快速失败的。另一方面,Hashtable提供了对键的列举(Enumeration)。
一般认为Hashtable是一个遗留的类。
Java集合类框架的最佳实践有哪些
根据应用的需要正确选择要使用的集合的类型对性能非常重要,比如:假如元素的大小是固定的,而且能事先知道,我们就应该用Array而不是ArrayList。
有些集合类允许指定初始容量。因此,如果我们能估计出存储的元素的数目,我们可以设置初始容量来避免重新计算hash值或者是扩容。
为了类型安全,可读性和健壮性的原因总是要使用泛型。同时,使用泛型还可以避免运行时的ClassCastException。
使用JDK提供的不变类(immutable class)作为Map的键可以避免为我们自己的类实现hashCode()和equals()方法。
编程的时候接口优于实现。
底层的集合实际上是空的情况下,返回长度是0的集合或者是数组,不要返回null。
线程的几种可用状态
线程在执行过程中,可以处于下面几种状态:
就绪(Runnable):线程准备运行,不一定立马就能开始执行。
运行中(Running):进程正在执行线程的代码。
等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束。
睡眠中(Sleeping):线程被强制睡眠。
I/O阻塞(Blocked on I/O):等待I/O操作完成。
同步阻塞(Blocked on Synchronization):等待获取锁。
死亡(Dead):线程完成了执行。
同步方法和同步代码块的区别是什么
在Java语言中,每一个对象有一把锁。线程可以使用synchronized关键字来获取对象上的锁。synchronized关键字可应用在方法级别(粗粒度锁)或者是代码块级别(细粒度锁)。
在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步
监视器和锁在Java虚拟机中是一块使用的。监视器监视一块同步代码块,确保一次只有一个线程执行同步代码块。每一个监视器都和一个对象引用相关联。线程在获取锁之前不允许执行同步代码.
如何确保N个线程可以访问N个资源同时又不导致死锁
使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了.
Lock与synchronized 的区别
-
ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候
线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情
a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
c)tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断
- synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中
- 在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;
synchronized:
在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。
ReentrantLock:
ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。
Atomic:
和上面的类似,不激烈情况下,性能比synchronized略逊,而激烈的时候,也能维持常态。激烈的时候,Atomic的性能会优于ReentrantLock一倍左右。但是其有一个缺点,就是只能同步一个值,一段代码中只能出现一个Atomic的变量,多于一个同步无效。因为他不能在多个Atomic之间同步。
Servlet不是线程安全的
要解释为什么Servlet为什么不是线程安全的,需要了解Servlet容器(即Tomcat)使如何响应HTTP请求的
当Tomcat接收到Client的HTTP请求时,Tomcat从线程池中取出一个线程,之后找到该请求对应的Servlet对象并进行初始化,之后调用service()方法。要注意的是每一个Servlet对象再Tomcat容器中只有一个实例对象,即是单例模式。如果多个HTTP请求请求的是同一个Servlet,那么着两个HTTP请求对应的线程将并发调用Servlet的service()方法。
线程安全
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。
网友评论