本文共 3879 字,大约阅读时间需要 12 分钟。
本篇所要介绍的是一家互联网企业,简称MD好了。一面是电面,二三面是face2face的技术面,4面是HR面。
一面的具体内容忘了记录了,大概面了45mins,问了30mins的NIO。
这里就来讲一讲NIO的一些知识点。NIO有三个部分:Selector, Channel和Buffer. 传统的IO基于字节流和字符流进行操作。而NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区,或者从缓冲区写入通道中。Selector用于监听多个通道的时间(比如打开,数据到达),因此,单个线程可以监听多个数据通道。 Selector内部原理实际是在做一个对所注册的Channel的轮询访问,不断的轮询,一旦轮询到一个channel有所注册的事情发生,比如数据来了,他就会站起来报告,交出一把钥匙(SelectionKey),让我妈通过这把钥匙来读取channel的内容。 NIO有一个重要的点是:Reactor模式。从网上抠来一张Reactor模式的模型图如下: Reactor模式有两大角色:Reactor和Handler. Reactor负责响应I/O事件;Handler负责非阻塞行为。至于Reactor模式的编码风格我就不赘述了。 前面说到了Selector的原理就是轮询,但是Java NIO中有个epoll()(linux系统下),这个对并发idle Connection会有大幅度的性能提升。epoll是linux下多路复用I/O接口Select/poll的增强版本。一般的轮询方式一个进程打开的FD(File Descriptor)是有一定限制的,FD_SETSIZE默认是2048,这个宏定义可以修改,但修改后的效果并不理想,对于那些需要支持上万连接数的IM服务器来说显然太少。epoll没有这个限制,可以通过”cat /proc/sys/fs/file-max”来查看支持的FD上限。博主本地机器是205822。epoll()有两种使用模式:LT(Level triggered)缺省,ET(Edge-triggered)高速。 epoll有一个著名的epoll-bug, 也可能会导致无效的状态的选择和100%的CPU利用率,要解决epoll-bug的唯一方法是回收旧的选择,将先前注册的通道实例转移到新创建的选择器上。当然如果使用一些框架比如:Netty, Mina. 上面的全问到了,问的还挺深的。最后问了为什么没用Netty之类的框架实现,还有有了解过其他的分布式框架么? 还问了一些Java基础题。 比如多线程的ThreadPollExecutor中的饱和策略有哪些? Ans:当线程池和队列都满了,则表明该线程池已达饱和状态。线程池怎么合理配置?
Ans:需要针对具体情况而具体处理,不同的任务类别应采用不同规模的线程池,任务类别可划分为CPU密集型任务、IO密集型任务和混合型任务。线程数的个数?(这个问题二面也问到了)
Nthreads = Ncpu*Ucpu*(1+W/C)。Ncpu是cpu的个数,可以通过Runtime.getRuntime.availableProcessors()获取。Ucpu是cpu的使用率。W/C=Wait-Time/Compute-Time. ps:Runtime.getRuntime是单例模式。二面先对简历上的内容询问了一番,问了一些问题,再此就不表了,接下去问了些Java类的问题。
1.线程池ThreadPoolExecutor以及Executors的一些问题。 2.ReentrantLock和synchronized的区别。从上面的比较中可以看出ReentrantLock比synchronized有很多的优点,但是JDK的开发团队还是推崇使用synchronized的方式,在JDK5中ReentrantLock比synchronized的性能优越很多,在JDK6中进行了很多的优化,ReentrantLock比synchronized只是稍微好点,而且ReentrantLock用的不好,比如Lock.lock之后再finally块中忘了Lock.unlock操作锁就永远得不到释放,而synchronized可以自动释放锁。而且经过JDK的开发团队的不懈努力,synchronized的性能会越来越好,比如:偏向锁,轻量级锁,自旋锁,锁粗化以及锁细化等优化操作。
3.ReentrantLock又称之为什么?怎么实现的?还有那些相同的?
ReentrantLock又称之为可重入锁,内部实现是基于AQS的(对于AQS不明白的小伙伴可以度娘了)。还有一些也是基于AQS的比如Semaphore,还有ReentrantReadWriteLock(独占锁+共享锁)。在ReentrantReadWriteLock中又涉及了一个锁降级的概念:先Hold写锁,再获取读锁,再释放写锁。没有锁升级的概念。(有关多线程的知识,可以翻看博主相关的文章,详情见参考资料1-4。) 4.sleep和wait的区别?为什么sleep在Thread中,wait在Object中? sleep不让出锁,它不会导致锁行为的改变,而wait会让出锁。Thread.sleep的调用不会影响锁的改变,所以放在sleep中,wait与锁有关所以放在Object中。 5.为什么Java中的锁是对象锁而不是线程锁? 这个问题比较抽象,主要是在对象锁的编程模型比较简单,线程锁其实也可以,但是编程模型太复杂,所以Java采用的对象锁。 6. 写一个单例模式。 博主写了一个内部类的单例模式,然后说了其他的写法比如:enum或者synchronized的写法,详情可以关注《》。 后面又问了一个问题:一个类中所有的方法和属性都标注为static的,那么这是一个单例么?面试官的语气暗示这不是一个单例,但的确想不出不是单例的地方,想了一会儿才明白过来这的确是一个单例的写法。单例是保证一个类的实例构造器只实现一次,而全部标注为static的类,它就只有类构造器,类构造器可以由JVM确保只执行一次,而且是线程安全的。 还有一道设计题:微信公众号拉取粉丝数量。三面的面试官照着简历问了一些问题,然后问了一些其他的和技术无关的,最后还问了一个技术类问题:怎么可以发生Full GC?
Full GC是指老年代发生了Stop the world的操作。详细可以参考《》。 比如在CMS执行时有初始标记、并发标记、重新标记、并发清除、并发重置5个步骤中,初始标记和重新标记都是stop the world的,当发生Concurrent Mode Failure时,会触发一次Serial-Old的操作。 可以在程序中通过设置-Xmx, -Xms等参数去触发,也可以通过System.gc()触发。当然有两个工具JConsole和JVsualVm中有“GC”的按钮。 (剩下的可以在留言区补充。)总结:无。(对这家公司太熟悉,不方便评价~~)
更多链接请关注:
参考资料
1. 2. 3. 4. 5. 6.