一、Java基础和高级
1.String类为什举是final的。
用处有三:1)常量池共享 2)线程安全 3)hashCode的键值最好选择
2.HashMap的源码,实现原理,底层结构。
HashMap底层使用数组+链表+红黑树实现,putVal在链表结点数大于8的时候自旋成红黑树,使得查询速度由O(n)提升为O(log n)
3.反射中,Class.forName 和classloader的区别
Class.forName不但会加载.class文件到jvm中还会执行static块代码,classloader只会将.class文件加载到jvm只有当执行newInstance的时候都会执行static代码。
4.session和cookie的区别和联系,session的生命周期,多个服务部署时session管理。
session存储在server端,cookie存储在客户端,cookie利用server返回的信息保存,cookie如果不设过期时间会长久保留在客户端,否则浏览器关闭的时候就会清空cookie信息。server当浏览器关闭的时候session注销。
多个服务器部署时可以有几种方案:1)session信息保存到cookie中,浏览器访问服务时带回即可。2)session请求粘贴,利用负载将同一个客户端的请求都发到一个server上。3)利用独立的redis缓存服务器来存储session信息,实现共享。redis可以做成集群,定时copy session信息
5.Java中的队列都有哪些,有什举区别。
Java中队列是一个先进先出的数据结构,通常不能随机访问,根据实现的方式不同主要有两种方式:数组 & 链表。如下图:
在并发编程中,保证线程安全的队列有阻塞队列和非阻塞队列之分。
阻塞队列在队列为空时或队列满的时候,获取元素的线程会等待队列变为非空,当队列为满时,存储元素的线程会等待队列可用。阻塞队列有以下几种:
- ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
- PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
- DelayQueue:一个使用优先级队列实现的无界阻塞队列。
- SynchronousQueue:一个不存储元素的阻塞队列。
- LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
- LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
阻塞队列的实现原理:添加元素的最后一行,notEmpty.signal();表示添加完元素后,调用signal通知等待的线程,队列不空了,可以来取东西的。移除元素的最后一行,也是这样的。
非阻塞的方式可以循环使用CAS的方式来实现,非阻塞队列的一个实现方式为concurrentLinkedQueue,这是一个基于链接节点的无界线程安全队列,入队和出队操作均使用CAS(Compare and set) 更新,这样允许多个线程并发执行,并且不会因为加锁而阻塞线程。
6.Java的内存模型以及GC算法
JAVA 1.8内存模型,JVM内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈,如下图所示:
1. 每个线程都有一个私有的栈,随着线程的创建而创建。栈里面存放着栈桢的东西,栈桢中存放了局部变量表。当栈调用深度超过JVM所允许的范围内之后,会抛出StackOverflow异常。
2.本地方法栈,这里面主要存放着虚拟机用到的native方法相关,一般不需要考虑。
3.PC寄存器,JVM支持多个线程同时运行,每个线程都有自己的程序计数器。
4. 堆,堆内存是JVM所有线程共享的部分,在虚拟机启动的时候创建。所有的对象和数组都在堆上进行分配,这部分的空间可以通过GC来回收,当申请不到空间后就会抛出OutOfMemoryError.
5.方法区,该区也是所有线程共享,主要存储类的信息、常量池、方法数据、方法代码等。方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。
GC算法主要分为:1)标记-清理。 2)复制算法。3)标记-整理算法 4)分类算法。
1)标记-清理,分为两个阶段,一:标记所需要回收的对象,二:回收被标记的对象。标记算法分为引用计数算法和可达性算法,由于引用计数算法无法解决循环引用 的问题,所以 一般会采用可达性算法。这种算法虽然清理了空间,但是空间不连续,利用率低。
2)复制算法,它将内存分为现块相等的区域,每次使用一块,当这一块使用完了,就将还存活的对象按照原有的顺序复制到另一块内存中,然后就当前空间一次性清理掉。内存开销较大。
3)标记-整理算法,标记跟1)一样,整理是将存活的对象内存一段移动,整理出一块较大的连续内存空间
7.Java7、Java8的新特性
Java7. 子类自动装箱。整型类型表达可用"_”拼接表达。catch多个exception. file文件读自动关闭。
Java8 lamada表达式。信息stream的使用
8.Java 数组和链表两种结构的操作效率,在哪些情冴下(从开头开始,从结尾开始,从中间开始),哪些操
作(插入,查找,删除)的效率高
数组查询时间O(1), 链表查询时间O(n) , 插入、删除效率高。
9.Java内存泄露的问题调查定位:jmap,jstack的使用等等
二、spring框架
spring框架中需要引用哪些jar包,以及这些jar包的用途
spring-web:开发web应用的时候会用到,包括自动载入webapplicationContext特性的类。
springg-core: 这个jar文件包含spring框架基本的核心工具类,
spring-aop:若要使用spring aop特性所需要的类
spring-beans:它包含访问配置文件,创建和管理bean以及进行控制反转的各个工具类。
spring-webmvc:包含mvc框架相关的核心类,包括国际化、标签、theme、视图展示、
spring-expression
spring-context: 这个包为spring提供扩展
srpingMVC的原理:
SpringMVC的工作原理图:
SpringMVC流程
1、 用户发送请求至前端控制器DispatcherServlet。
2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、 DispatcherServlet调用HandlerAdapter处理器适配器。
5、 HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6、 Controller执行完成返回ModelAndView。
7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9、 ViewReslover解析后返回具体View。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、 DispatcherServlet响应用户。
组件说明:
以下组件通常使用框架提供实现:
DispatcherServlet:作为前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,降低组件之间的耦合性,提高每个组件的扩展性。
HandlerMapping:通过扩展处理器映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
HandlAdapter:通过扩展处理器适配器,支持更多类型的处理器。
ViewResolver:通过扩展视图解析器,支持更多类型的视图解析,例如:jsp、freemarker、pdf、excel等。
springMVC注解的意思:
spring中beanFactory和ApplicationContext的联系和区别
beanFactory是spring最底层的功能,提供了容器功能,只提供了实例化对象和取对象的基本功能,它是延迟加载的策略,只有当调用的时候spring才会创建对象。
ApplicationContext继承beanFactory接口,是一种更高级的容器,提供了更多的功能:1)国际化 2)访问资源 3)消息发送及响应。4)aop.
spring注入的几种方式
三种方式:1)属性的set方法注入 2)使用构造器注入 3)使用Field注入(注解方式)
spring如何实现事物管理的
spring aop 面向切面编程实现需要开户事务的service拦截。
springIOC和AOP的原理
IOC 原理参考:https://mp.youkuaiyun.com/postedit/97909819
AOP原理参考:https://mp.youkuaiyun.com/postedit/97926469
spring中循环注入的方式
Spring 循环注入其实就是循环依赖。比如多个对象之间的相互依赖。出现循环注入的方式有三种:
构造器注入,setting注入,自动注入。
构造器注入: 此依赖无法解决、
setting是通过spring容器提前暴露刚完成构造器注入但是未完成其他步骤(如setting注入等)的bean来完成的,而且只能解决单例作用域的bean循环依赖。
Spring的beanFactory和factoryBean的区别
beanFactory是Ioc容器的核心接口,它的职责包括:实例化 、定位、配置应用程序中的对象的建立这些对象间的依赖、
ApplicationContext是beanFactory的扩展,功能得到进一步的增强,与aop集成,用得最多的beanFactory的子类是ClassPathXmlApplicationContext..
Spring为我们提供了两种bean,一种是普通的bean,一种是FactoryBean,实现了FactoryBean接口的类有能力改变bean的行为。FactoryBean希望你实现了它之后返回一些内容。spring会按照这些内容生成bean
Spring的事务隔离级别,实现原理
Spring事务的隔离级别是与数据库保持一致的
隔离级别 | 隔离级别的值 | 导致的问题 |
Read-Uncommitted | 0 | 导致脏读 |
Read-Committed | 1 | 避免脏读,允许不可重复读和幻读 |
Repeatable-Read | 2 | 避免脏读,不可重复读,允许幻读 |
Serializable | 3 | 串行化读,事务只能一个一个执行,避免了脏读、不可重复读、幻读。执行效率慢,使用时慎重 |
Spring事务的传播机制,有大概7种:
常量名称 | 常量解释 |
PROPAGATION_REQUIRED | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是 Spring 默认的事务的传播。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。新建的事务将和被挂起的事务没有任何关系,是两个独立的事务,外层事务失败回滚之后,不能回滚内层事务执行的结果,内层事务失败抛出异常,外层事务捕获,也可以不处理回滚操作 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。 |
对 Spring 的理解,非单例注入的原理?它的生命周期?循环注入的原理,aop 的实现原理,说说 aop 中的几个术语,它们是怎举相互工作的?
spring boot特性,优势,适用场景等
三、java多线程常见问题
1.Java创建线程后,直接调用start()方法和run()的区别
在同一个线程内部可多次执行run方法,start只能启动一次。
start真正的多线程运行,这里无须等待run方法体执行完成,就可以继续执行新的thread.start方法 。
run只是普通方法来使用,程序还要顺序执行,要等待run方法执行完毕后,才可以继续执行下面的代码。
2.常用的线程池模式以及不同线程池的使用场景
java使用Executor管理线程池,底层使用ThreadPoolExecutor
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
corePoolSize:池中所保存的线程数,包括空闲线程
maximumPoolSize-池中允许的最大线程数。
keepAliveTime - 当线程数大于核心时,多于的空闲线程最多存活时间
unit - keepAliveTime 参数的时间单位。
workQueue - 当线程数目超过核心线程数时用于保存任务的队列
RejectedExecutionHandler 线程的饱和策略,默认的是abortPolicy(表示无法处理新任务),这时会抛出handler里面的rejectedExecution方法通知调用者,并抛出rejectedExecutionException。还有另外3个策略:DiscardPolicy/DiscardOldestPolicy/CallerRunsPolicy,分别解释如下:
(1)CallerRunsPolicy:用调用者所在的线程由处理原任务改为处理新任务
(2)DiscardPolicy:抛弃新任务
(3)DiscardOldestPolicy:丢弃队列中最近的任务,并执行新任务
其它的还有几个executor实现类:
newCachedThreadPoo l创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO,
3.newFixedThreadPool此种线程池如果线程数达到最大值后会怎举办,底层原理。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待,它采用的是LinkedBlockingQueue,是无界队列,任务可以一起添加进去。所以newFixedThreadPool会保持nThread个线程在线程池中,而且是按顺序从LinkedBlockingQueue中取任务。若提交的任务比完成的任务时间快时,会一直添加任务,导致LinkedBlockingQueue一直增长,消耗大量内存。
4.多线程之间通信的同步问题。
多线程之间的通信主要是为了解决同步和异步的问题。
实现方法:线程间的通信可以通过:1)互斥锁(mutex)。 2)信号量(semphore)。 3)条件变量(condition)。4)读写锁(reader-writer lock)
互斥锁:是一个变量,它有Lock和unlock两个状态。互斥锁一般会被设置成全局变量,打开的互斥锁可以由某个线程获得,一旦获得,这个互斥锁会再锁上,这样,只有该线程才能有权打开。其它想要获取互斥锁的线程,要等到互斥锁再次打开的时候。
信号量。信号量和互斥锁的区别是,互斥锁只允许一个线程进行临界区,而信号量允许多个线程同时进入临界区。
条件变量:condition
读写锁:volatile修饰的变量直接存放在main memory里面,volatile能保证所修饰的变量对于多个线程可见,即只要被修改,其它线程就一定能读到最新的值。
JAVA通过wait . notify, notifyAll三个方法来解决线程间通信问题。wait可以使调用该方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到再次被唤醒。notify可以使调用该方法的线程获取共享资源的锁,进行运行状态。
5.了解可重入锁的含义以及ReentrantLock 和synchronized的区别
可重入锁的举例:当一个线程执行到synchronized方法method1时,而method1中调用了另一个synchronized方法,method2,此线程不需要重新申请一把锁可以直接执行method2.
Synchronized 和 ReentrantLock都是可重入锁。Synchronized是不可中断锁,而ReentrantLock是可中断锁,如果线程a正在执行锁中的代码时间过长,可以自己中断或者在别的线程上中中断它。而synchronized是不可以的。Synchornized和ReentrantLock都是非公平锁,但是ReentrantLock可以开启公平锁。
6.同步的数据结构,例如 concurrentHashMap 的源码理解以及内部实现原理,为什举他是同步的且效率高
ConcurrentHashMap:
static class Node<K,V>{
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
}
7.atomicinteger 和volatile等线程安全操作的关键字的理解和使用
8.线程间通信,wait和notify