java面试(五)
1、缓存是什么?
缓存是指可以进行高速数据交换的存储器,它先于内存与CPU交换数据,因此速率很快。
2、给个秒杀场景,如何防止超卖?
(1)悲观锁思路。悲观锁,也就是在修改数据的时候,采用锁定状态,排斥外部请求的修改。遇到加锁的状态,就必须等待。
(2)FIFO队列思路。直接将请求放入队列中的,采用FIFO(First Input First Output,先进先出),这样的话,我们就不会导致某些请求永远获取不到锁。
(3)乐观锁思路。乐观锁,是相对于“悲观锁”采用更为宽松的加锁机制,大都是采用带版本号(Version)更新。实现就是,这个数据所有请求都有资格去修改,但会获得一个该数据的版本号,只有版本号符合的才能更新成功,其他的返回抢购失败。这样的话,我们就不需要考虑队列的问题,不过,它会增大CPU的计算开销。但是,综合来说,这是一个比较好的解决方案。
3、hashmap底层数据结构,hastable,concurrenthashmap如何实现线程安全的?
HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫做Entry。这些个键值对(Entry)分散存储在一个数组当中,这个数组就是HashMap的主干。HashMap数组每一个元素的初始值都是Null。
HashTable和ConcurrentHashMap都是线程安全的容器。
hastable实现线程安全:这些对容器中数据进行操作的方法都被synchronized关键字修饰,和最开始的案例一样,这种jdk自带的内置锁可以使得被synchronized关键字修饰的方法体和代码块一次只能被一个线程执行,也就保证了线程安全的问题。
ConcurrentHashMap实现线程安全:ConcurrentHashMap的底层数据结构实现原理其实和HashMap没什么两样,都是数组+链表+红黑树,使用分段锁的思想进行细分化。
4、用过线程池吗?线程池的参数,提交一个任务时的过程,核心线程会被回收吗?拒绝策略
corePoolSize:核心线程数
核心线程会一直存活,及时没有任务需要执行。
当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理。
设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭。
queueCapacity:任务队列容量(阻塞队列)
核心线程数达到最大时,新任务会放在队列中排队等待执行。
maxPoolSize:最大线程数
当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务。
当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常。
keepAliveTime:线程空闲时间
当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize。
如果allowCoreThreadTimeout=true,则会直到线程数量=0。
allowCoreThreadTimeout:允许核心线程超时
rejectedExecutionHandler:任务拒绝处理器
拒绝策略
(1)AbortPolicy 丢弃任务,抛运行时异常。(2)CallerRunsPolicy 执行任务。(3)DiscardPolicy 忽视,什么都不会发生。(4)DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务。实现RejectedExecutionHandler接口,也可自定义处理器。
5、看过什么源码?讲一下spring,aop和ioc,会什么设计模式?单例,写一个
spring 的优点?
1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦
2.可以使用容易提供的众多服务,如事务管理,消息服务等
3.容器提供单例模式支持
4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能
5.容器提供了众多的辅助类,能加快应用的开发
6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等
7.spring属于低侵入式设计,代码的污染极低
8.独立于各种应用服务器
9.spring的DI机制降低了业务对象替换的复杂性
10.Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部
依赖注入(Dependecy Injection)和控制反转(Inversion of Control)是同一个概念,具体的讲:当某个角色需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在spring中创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由spring来完成,然后注入调用者 ,因此也称为依赖注入。
面向切面编程(AOP)完善spring的依赖注入(DI),面向切面编程在spring中主要表现为两个方面
1.面向切面编程提供声明式事务管理
2.spring支持用户自定义的切面
面向切面编程(aop)是对面向对象编程(oop)的补充,
面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。
AOP从程序运行角度考虑程序的结构,提取业务处理过程的切面,oop是静态的抽象,aop是动态的抽象,
是对应用执行过程中的步骤进行抽象,,从而获得步骤之间的逻辑划分。
aop框架具有的两个特征:
1.各个步骤之间的良好隔离性
2.源代码无关性
单例模式:
第 1 种:懒汉式单例
该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。代码如下:
- public class LazySingleton
- {
- private static volatile LazySingleton instance=null; //保证 instance 在所有线程中同步
- private LazySingleton(){} //private 避免类在外部被实例化
- public static synchronized LazySingleton getInstance()
- {
- //getInstance 方法前加同步
- if(instance==null)
- {
- instance=new LazySingleton();
- }
- return instance;
- }
- }
注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。
第 2 种:饿汉式单例
该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。
- public class HungrySingleton
- {
- private static final HungrySingleton instance=new HungrySingleton();
- private HungrySingleton(){}
- public static HungrySingleton getInstance()
- {
- return instance;
- }
- }
饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题
6、jvm内存模型,存放什么,什么线程私有,线程共享,如何判断一个对象需要被回收, 可以作为GCRoot的对象有哪些?
Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。
线程私有:
栈(局部变量,函数的参数)
线程局部存储(Thread Local Storage,TLS)。有限的容量
寄存器 (执行流的基本数据)
线程共享(进程所有):
全局变量
堆上的数据
函数里的静态变量
程序代码,任何线程都有权利读取并执行任何代码
打开文件,A线程打开的文件可以由B线程读写
判断一个对象需要被回收:
引用可达法法,程序运行从开始,每次引用对象,都将对引用的对象进行连接起来,到最后形成一张网,没有在这张网上的对象则被认为是垃圾对象。 还有引用计数法,对于对象的引用,每引用一次计数器加一,引用失败,计数器减一,当计数器一段时间为0,则可以被认为是垃圾。
GC管理的主要区域是Java堆,一般情况下只针对堆进行垃圾回收。方法区、栈和本地方法区不被GC所管理,因而选择这些区域内的对象作为GC roots,被GC roots引用的对象不被GC回收。
可以作为GCRoot的对象有哪些?
虚拟机栈中引用的对象(即栈帧中的本地变量表)
2.方法区中的常量引用的对象
3.方法区中的类静态属性引用的对象
4.本地方法栈中的JNI(Native方法)的引用对象
5.活跃线程的引用对象
求职面试题&解析