基础知识面试题(二)

8、java8的ConcurrentHashMap为什么放了分段锁,有什么问题么,如果你来设计,你如何设计

ConcurrentHashMap通过将一个大的数据结构分解为多个小的数据结构,然后对每个小的数据结构加锁来实现线程安全,这种方式成为分段锁。但是分段锁的实现方式也存在一些问题:首先,由于每个小的数据结构都需要加锁,因此锁的竞争会增加,其次,由于每个小的数据结构的大小是固定的,因此在高并发的情况下,容易出现热点数据的问题,即某个小的数据结构被频繁的访问,从而导致该小的数据结构的锁竞争非常激烈,这会导致整个ConcurrentHashMap的性能下降,另外,分段锁的实现也存在一些复杂性,例如需要考虑锁的重入,死锁等问题。

为了解决分段锁的局限性,java8中重新实现了ConcurrentHashMap,放弃了分段锁的机制,而采用了全新的CAS和synchronized组合的方式来保证并发安全,具体来说,java8中的ConcurrentHshMap采用了以下方式来实现线程安全:

数组+链表+红黑树的数据结构:ConcurrentHashMap的内部数据结构由一个数组和链表/红黑树组成,其中数组的每个元素都指向一个链表/红黑树,链表/红黑树存储了实际的键值对。

CAS(Compare and Swap)操作:在ConcurrentHashMap中,对于需要修改的操作,例如put,remove等,会采用CAS操作来实现。具体来说,CAS操作会先比较当前节点的值和期望值是否相等,如果相等,则将当前节点的值修改为新值,否则不进行任何操作。

synchronized块:为了保证数据组的一致性,ConcurrentHashMap会在修改数组时使用synchronized块来保证同步性,具体来说,ConcurrnetHashMap会将整个数组进行分段,每个内部采用synchronized块来保证同步性,不同段之间可以并发地进行操作。

9、有没有有顺序的map实现类,如果有,他们是怎么保证有序的

TreeMap,实现SortedMap接口,能够把保存的记录根据键排序,默认是按键值的升序排序,当用Iterator遍历TreeMap时,得到的记录是排过序的。是基于比较器Comparator来实现有序的。

LinkedHashMap,是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的数据。是基于链表来实现数据插入有序的。

10、抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接口么

抽象类和接口不能实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象

抽象类要被子类继承,接口要被类实现

接口中只能做方法生命,抽象类中可以做方法声明,也可以做方法实现。

接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通的变量

抽象类中的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类,同样,一个实现接口的时候,如果不能全部实现接口方法,那么该类也只能是抽象类

抽象方法只能声明,不能实现

抽象类中可以没有抽象方法

如果一个类里有抽象方法,那么这个类只能是抽象类

抽象方法要被实现,所以不能是静态的,也不能是私有的,

接口可继承接口,并可多继承接口,但类只能是单根继承,类可以实现多个接口

11、继承和聚合的区别在哪

继承指的是一个类继承另外一个类的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系,在java中此类关系通过关键字extends明确标识。

聚合体现的是整体与部分、拥有的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期。

12、IO模型有哪些,讲讲你理解的nio,他和bio,aio的区别是什么,谈谈reactor模型

nio同步非阻塞IO:用户进程发起一个IO操作后,可做其他事情,但用户进程需要经常询问IO操作是否完成,这样造成不必要的CPU资源浪费。

bio同步阻塞IO:用户进程发起一个IO操作后,必须等待IO操作的真正完成后,才能继续运行。

AIO异步非阻塞IO:用户进程发起一个IO操作后,立即返回,等IO操作真的完成之后,应用程序会得到IO操作完成的通知。

同步阻塞BIO

我们需要知道,内核在处理数据的时候其实分成了两个阶段:数据准备,数据复制

在网络IO中,数据准备可能是客户端还有部分数据还没有发送,或者正在发送的途中,当前内核Buffer中的数据并不完整;而数据复制则是将内核Buffer中的数据复制到用户态的Buffer中去;

当调用线程发起read系统调用时,如果此时内核数据还没有Ready,调用线程会阻塞住,等待内核Buffer的数据,内核数据准备就绪之后,会将内核态Buffer的数据复制到用户态Buffer中,这个过程中调用线程仍然是阻塞的,知道数据复制完成。

同步非阻塞NIO

数据准备阶段,此时用户线程发起read系统调用,此时内核会立即返回一个错误,告诉用户态数据还没有Ready,然后用户线程不听的发起请求,询问内核当前数据的状态。

数据复制阶段,此时用户线程还在不断的发起请求,但是当数据Ready之后,用户线程就会陷入阻塞,直到数据从内核态复制到用户态。

如果内核态的数据没有Ready,用户线程不会阻塞,但是如果内核态数据Ready了,集市当前的IO模型是同步非阻塞,用户线程仍然会进入阻塞状态,直到数据复制完成,并不是绝对的非阻塞。

NIO的优点是实时性好,内核态数据没有Ready会立即返回,但是频繁的轮询内核,会占用大量的CPU资源,降低效率

异步非阻塞IO

用户线程发起read系统调用之后,无论内核Buffer数据是否Ready,都不会阻塞,而是立即返回。

内核在收到请求之后,会立即准备数据,准备好了并且复制完成之后会由内核发送一个Signal给用户线程,或者回掉用户线程注册的接口进行通知(read/write方法都是异步的,完成后会主动调用回调函数)。用户线程收到通知之后就可以去读取用户态Buffer的数据了。

Reactor模式也叫做反应器设计模式,是一种为处理服务请求并发提交到一个或多个服务处理器的事件设计模式。

Reactor模式主要由Reactor和处理器Handler两个核心部分组成,模型中定义了三种角色:

Reactor:派发器,负责监听和分配事件,并将事件分派给对应的Handler,新的事件包含连接建立就绪,读就绪,写就绪等。

Acceptor:请求连接器,处理客户端新连接。Reactor接收到client端的连接事件后,会将其转发给Acceptor,由Acceptor接收client的连接,创建对应的Handler,并向Reactor注册此Handler

Handler:请求处理器,负责事件的处理,将自身与事件绑定,执行非阻塞读写任务,完成channel的读入,完成处理业务逻辑后,负责将结果写出channel,可用资源池来管理。

主要由三种模式:

单Reactor单线程:前台接待员和服务员是同一个人,全程为客户服务;

单Reactor多线程:1个前台接待员,多个服务员,接待员只负责接待

主从Reactor多线程:多个前台接待员,多个服务员

Reactor模型优点:

响应快,不必为单个同步事件所阻塞,虽然Reactor本身依然是同步的

可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销

扩展性好,可以方便的通过增加Reactor实例个数来充分利用CPU资源

复用性好,Reactor模型本身与具体事件处理逻辑无关,具有很高的复用性。

13、反射的原理,反射创建类实例的三种方式是什么。

java的反射是利用加载到jvm中的.class文件来进行操作的,.class文件包含java类中行的所有信息,当你知道某个类的具体信息时,可以使用反射获取Class,然后进行各种操作。

对象调用getClass()方法来获取

类名.class的方法获取,该方法最为安全可靠,程序性更高

通过class对象的forName()静态方法来获取,用的最多,但可能抛出ClassNotFoundException异常

14、反射中,Class.forName 和 ClassLoader 区别 。

都可以用来对类进行加载

class.forName("className")

其实这种方法调用的是:Class.forName(className,true,ClassLoader,getCallerClassLoader())方法

其中className需要加载类的名称,true是否对class进行初始化(需要序列化initialize),classLoader对应的类加载器

ClassLoader.loadClass("className")

其实这种方法调用的是ClassLoader.loadClass(name,false)方法

其中name是需要加载的类的名称,false这个类加载以后是否需要去连接

可见Class.forName出了将类.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块

而ClassLoader只干了一件事,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static。

15、描述动态代理的几种实现方式,分别说出相应的优缺点。

动态代理就是利用反射和字节码的技术,在运行期创建指定接口或类的子类(动态代理)以及其实例对象的技术,以达到无侵入性增强代码的效果

JDK原生动态代理

其中关键的两个要素为:

Proxy:newProxyInstance()生成代理对象

InvocationHandler:invoke()增强方法

CGLIB(code generation library)动态代理

其中关键的两个要素为:

Enhance:create()生成代理对象

MethondInterceptor:intercept()增强方法

两者优缺点:

JDK原生动态代理:java原生支持,不需要任何外部依赖,但只能基于接口进行代理

CGLIB动态代理:通过继承的方式进行处理,无论目标对象是否实现接口都可以代理,但无法处理final的情况

16、final 的用途。

final意为最终的

修饰类:表示类不可被继承

修饰方法:表示方法不可被子类覆盖,但是可以被重载

修饰变量:表示变量一旦被赋值就不可以更改它的值

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值