本文作者locklock: 伯樂在線- 朱小廝。未經(jīng)作者許可,禁止轉(zhuǎn)載!
歡迎加入伯樂在線 專欄作者。
本篇所要介紹locklock的是一家互聯(lián)網(wǎng)企業(yè),簡稱MD好了。一面是電面,二三面是face2face的技術(shù)面,4面是HR面。
一面
一面的具體內(nèi)容忘了記錄了,大概面了45mins,問了30mins的NIO。
這里就來講一講NIO的一些知識點。NIO有三個部分:Selector, Channel和Buffer. 傳統(tǒng)的IO基于字節(jié)流和字符流進(jìn)行操作。而NIO基于Channel和Buffer(緩沖區(qū))進(jìn)行操作,數(shù)據(jù)總是從通道讀取到緩沖區(qū),或者從緩沖區(qū)寫入通道中。Selector用于監(jiān)聽多個通道的時間(比如打開,數(shù)據(jù)到達(dá)),因此,單個線程可以監(jiān)聽多個數(shù)據(jù)通道。
Selector內(nèi)部原理實際是在做一個對所注冊的Channel的輪詢訪問,不斷的輪詢,一旦輪詢到一個channel有所注冊的事情發(fā)生,比如數(shù)據(jù)來了,他就會站起來報告,交出一把鑰匙(SelectionKey),讓我媽通過這把鑰匙來讀取channel的內(nèi)容。
NIO有一個重要的點是:Reactor模式。
從網(wǎng)上摳來一張Reactor模式的模型圖如下:
Reactor模式有兩大角色:Reactor和Handler. Reactor負(fù)責(zé)響應(yīng)I/O事件;Handler負(fù)責(zé)非阻塞行為。至于Reactor模式的編碼風(fēng)格我就不贅述了。
前面說到了Selector的原理就是輪詢,但是Java NIO中有個epoll()(linux系統(tǒng)下),這個對并發(fā)idle Connection會有大幅度的性能提升。epoll是linux下多路復(fù)用I/O接口Select/poll的增強版本。一般的輪詢方式一個進(jìn)程打開的FD(File Deor)是有一定限制的,F(xiàn)D_SETSIZE默認(rèn)是2048,這個宏定義可以修改,但修改后的效果并不理想,對于那些需要支持上萬連接數(shù)的IM服務(wù)器來說顯然太少。epoll沒有這個限制,可以通過”cat /proc/sys/fs/file-max”來查看支持的FD上限。博主本地機器是205822。epoll()有兩種使用模式:LT(Level triggered)缺省,ET(Edge-triggered)高速。
epoll有一個著名的epoll-bug, 也可能會導(dǎo)致無效的狀態(tài)的選擇和100%的CPU利用率,要解決epoll-bug的唯一方法是回收舊的選擇,將先前注冊的通道實例轉(zhuǎn)移到新創(chuàng)建的選擇器上。當(dāng)然如果使用一些框架比如:Netty, Mina.
面的全問到了,問的還挺深的。最后問了為什么沒用Netty之類的框架實現(xiàn),還有有了解過其他的分布式框架么?
還問了一些Java基礎(chǔ)題。
比如多線程的ThreadPollExecutor中的飽和策略有哪些?
Ans:當(dāng)線程池和隊列都滿了,則表明該線程池已達(dá)飽和狀態(tài)。
ThreadPoolexecutor.AbortPolicy:處理程序遭到拒絕,則直接拋出運行時異常 RejectedExecutionException。(默認(rèn)策略)ThreadPoolexecutor.CallerRunsPolicy:調(diào)用者所在線程來運行該任務(wù),此策略提供簡單的反饋控制機制,能夠減緩新任務(wù)的提交速度。ThreadPoolExecutor.DiscardPolicy:無法執(zhí)行的任務(wù)將被刪除。ThreadPoolExecutor.DiscardOldestPolicy:如果執(zhí)行程序尚未關(guān)閉,則位于工作隊列頭部的任務(wù)將被刪除,然后重新嘗試執(zhí)行任務(wù)(如果再次失敗,則重復(fù)此過程)。
線程池怎么合理配置?
Ans:需要針對具體情況而具體處理,不同的任務(wù)類別應(yīng)采用不同規(guī)模的線程池,任務(wù)類別可劃分為CPU密集型任務(wù)、IO密集型任務(wù)和混合型任務(wù)。
對于CPU密集型任務(wù):線程池中線程個數(shù)應(yīng)盡量少,不應(yīng)大于CPU核心數(shù);對于IO密集型任務(wù):由于IO操作速度遠(yuǎn)低于CPU速度,那么在運行這類任務(wù)時,CPU絕大多數(shù)時間處于空閑狀態(tài),那么線程池可以配置盡量多些的線程,以提高CPU利用率;對于混合型任務(wù):可以拆分為CPU密集型任務(wù)和IO密集型任務(wù),當(dāng)這兩類任務(wù)執(zhí)行時間相差無幾時,通過拆分再執(zhí)行的吞吐率高于串行執(zhí)行的吞吐率,但若這兩類任務(wù)執(zhí)行時間有數(shù)據(jù)級的差距,那么沒有拆分的意義。
線程數(shù)的個數(shù)?(這個問題二面也問到了)
Nthreads = Ncpu*Ucpu*(1+W/C)。Ncpu是cpu的個數(shù),可以通過Runtime.getRuntime.availableProcessors()獲取。Ucpu是cpu的使用率。W/C=Wait-Time/Compute-Time. ps:Runtime.getRuntime是單例模式。
二面
二面先對簡歷上的內(nèi)容詢問了一番,問了一些問題,再此就不表了,接下去問了些Java類的問題。
1.線程池ThreadPoolExecutor以及Executors的一些問題。
2.ReentrantLock和synchronized的區(qū)別。
ReentrantLock可以中斷地獲取鎖(void lockInterruptibly() throws InterruptedException)ReentrantLock可以嘗試非阻塞地獲取鎖(boolean tryLock())ReentrantLock可以超時獲取鎖。通過tryLock(timeout, unit),可以嘗試獲得鎖,并且指定等待的時間。ReentrantLock可以實現(xiàn)公平鎖。通過new ReentrantLock(true)實現(xiàn)。ReentrantLock對象可以同時綁定多個Condition對象,而在synchronized中,鎖對象的的wait(), notify(), notifyAll()方法可以實現(xiàn)一個隱含條件,如果要和多于一個的條件關(guān)聯(lián)的對象,就不得不額外地添加一個鎖,而ReentrantLock則無需這樣做,只需要多次調(diào)用newCondition()方法即可。
從上面的比較中可以看出ReentrantLock比synchronized有很多的優(yōu)點,但是JDK的開發(fā)團隊還是推崇使用synchronized的方式,在JDK5中ReentrantLock比synchronized的性能優(yōu)越很多,在JDK6中進(jìn)行了很多的優(yōu)化,ReentrantLock比synchronized只是稍微好點,而且ReentrantLock用的不好,比如Lock.lock之后再finally塊中忘了Lock.unlock操作鎖就永遠(yuǎn)得不到釋放,而synchronized可以自動釋放鎖。而且經(jīng)過JDK的開發(fā)團隊的不懈努力,synchronized的性能會越來越好,比如:偏向鎖,輕量級鎖,自旋鎖,鎖粗化以及鎖細(xì)化等優(yōu)化操作。
3.ReentrantLock又稱之為什么?怎么實現(xiàn)的?還有那些相同的?
ReentrantLock又稱之為可重入鎖,內(nèi)部實現(xiàn)是基于AQS的(對于AQS不明白的小伙伴可以度娘了)。還有一些也是基于AQS的比如Semaphore,還有ReentrantReadWriteLock(獨占鎖+共享鎖)。在ReentrantReadWriteLock中又涉及了一個鎖降級的概念:先Hold寫鎖,再獲取讀鎖,再釋放寫鎖。沒有鎖升級的概念。(有關(guān)多線程的知識,可以翻看博主相關(guān)的文章,詳情見參考資料1-4。)
4.sleep和wait的區(qū)別?為什么sleep在Thread中,wait在Object中?
sleep不讓出鎖,它不會導(dǎo)致鎖行為的改變,而wait會讓出鎖。Thread.sleep的調(diào)用不會影響鎖的改變,所以放在sleep中,wait與鎖有關(guān)所以放在Object中。
5.為什么Java中的鎖是對象鎖而不是線程鎖?
這個問題比較抽象,主要是在對象鎖的編程模型比較簡單,線程鎖其實也可以,但是編程模型太復(fù)雜,所以Java采用的對象鎖。
6. 寫一個單例模式。
博主寫了一個內(nèi)部類的單例模式,然后說了其他的寫法比如:enum或者synchronized的寫法,詳情可以關(guān)注《設(shè)計模式:單例模式(Singleton)》。
后面又問了一個問題:一個類中所有的方法和屬性都標(biāo)注為static的,那么這是一個單例么?面試官的語氣暗示這不是一個單例,但的確想不出不是單例的地方,想了一會兒才明白過來這的確是一個單例的寫法。單例是保證一個類的實例構(gòu)造器只實現(xiàn)一次,而全部標(biāo)注為static的類,它就只有類構(gòu)造器,類構(gòu)造器可以由JVM確保只執(zhí)行一次,而且是線程安全的。
還有一道設(shè)計題:微信公眾號拉取粉絲數(shù)量。
三面
三面的面試官照著簡歷問了一些問題,然后問了一些其他的和技術(shù)無關(guān)的,最后還問了一個技術(shù)類問題:怎么可以發(fā)生Full GC?
Full GC是指老年代發(fā)生了Stop the world的操作。詳細(xì)可以參考《Java堆內(nèi)存 》。
比如在CMS執(zhí)行時有初始標(biāo)記、并發(fā)標(biāo)記、重新標(biāo)記、并發(fā)清除、并發(fā)重置5個步驟中,初始標(biāo)記和重新標(biāo)記都是stop the world的,當(dāng)發(fā)生Concurrent Mode Failure時,會觸發(fā)一次Serial-Old的操作。
可以在程序中通過設(shè)置-Xmx, -Xms等參數(shù)去觸發(fā),也可以通過System.gc()觸發(fā)。當(dāng)然有兩個工具JConsole和JVsualVm中有“GC”的按鈕。
?。ㄊO碌目梢栽诹粞詤^(qū)補充。)
總結(jié):無。(對這家公司太熟悉,不方便評價~~)
更多鏈接請關(guān)注:
這里有一份面經(jīng),請查收(一)
這里有一份面經(jīng),請查收(二)
這里有一份面經(jīng),請查收(三)
這里有一份面經(jīng),請查收(四)
這里有一份面經(jīng),請查收(五)
這里有一份面經(jīng),請查收(六)
打賞支持我寫出更多好文章,謝謝!
打賞作者 打賞支持我寫出更多好文章,謝謝!
任選一種支付方式
1贊收藏評論
關(guān)于作者:朱小廝
Java相關(guān)技術(shù)愛好者 個人主頁· 我的文章· 8·