天天看點

這裡有一份面筋請查收(七)

本篇所要介紹的是一家網際網路企業,簡稱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:當線程池和隊列都滿了,則表明該線程池已達飽和狀态。

ThreadPoolExecutor.AbortPolicy:處理程式遭到拒絕,則直接抛出運作時異常 RejectedExecutionException。(預設政策)

ThreadPoolExecutor.CallerRunsPolicy:調用者所線上程來運作該任務,此政策提供簡單的回報控制機制,能夠減緩新任務的送出速度。

ThreadPoolExecutor.DiscardPolicy:無法執行的任務将被删除。

ThreadPoolExecutor.DiscardOldestPolicy:如果執行程式尚未關閉,則位于工作隊列頭部的任務将被删除,然後重新嘗試執行任務(如果再次失敗,則重複此過程)。

線程池怎麼合理配置?

Ans:需要針對具體情況而具體處理,不同的任務類别應采用不同規模的線程池,任務類别可劃分為CPU密集型任務、IO密集型任務和混合型任務。

對于CPU密集型任務:線程池中線程個數應盡量少,不應大于CPU核心數;

對于IO密集型任務:由于IO操作速度遠低于CPU速度,那麼在運作這類任務時,CPU絕大多數時間處于空閑狀态,那麼線程池可以配置盡量多些的線程,以提高CPU使用率;

對于混合型任務:可以拆分為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可以中斷地擷取鎖(void lockInterruptibly() throws InterruptedException)

ReentrantLock可以嘗試非阻塞地擷取鎖(boolean tryLock())

ReentrantLock可以逾時擷取鎖。通過tryLock(timeout, unit),可以嘗試獲得鎖,并且指定等待的時間。

ReentrantLock可以實作公平鎖。通過new ReentrantLock(true)實作。

ReentrantLock對象可以同時綁定多個Condition對象,而在synchronized中,鎖對象的的wait(), notify(), notifyAll()方法可以實作一個隐含條件,如果要和多于一個的條件關聯的對象,就不得不額外地添加一個鎖,而ReentrantLock則無需這樣做,隻需要多次調用newCondition()方法即可。

從上面的比較中可以看出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. 寫一個單例模式。

後面又問了一個問題:一個類中所有的方法和屬性都标注為static的,那麼這是一個單例麼?面試官的語氣暗示這不是一個單例,但的确想不出不是單例的地方,想了一會兒才明白過來這的确是一個單例的寫法。單例是保證一個類的執行個體構造器隻實作一次,而全部标注為static的類,它就隻有類構造器,類構造器可以由JVM確定隻執行一次,而且是線程安全的。

還有一道設計題:微信公衆号拉取粉絲數量。

三面的面試官照着履歷問了一些問題,然後問了一些其他的和技術無關的,最後還問了一個技術類問題:怎麼可以發生Full GC?

比如在CMS執行時有初始标記、并發标記、重新标記、并發清除、并發重置5個步驟中,初始标記和重新标記都是stop the world的,當發生Concurrent Mode Failure時,會觸發一次Serial-Old的操作。

可以在程式中通過設定-Xmx, -Xms等參數去觸發,也可以通過System.gc()觸發。當然有兩個工具JConsole和JVsualVm中有“GC”的按鈕。

(剩下的可以在留言區補充。)

總結:無。(對這家公司太熟悉,不友善評價~~)

更多連結請關注:

<a href="http://blog.csdn.net/u013256816/article/details/51780920">這裡有一份面筋請查收(一)</a>

<a href="http://blog.csdn.net/u013256816/article/details/51787470">這裡有一份面筋請查收(二)</a>

<a href="http://blog.csdn.net/u013256816/article/details/51793563">這裡有一份面筋請查收(三)</a>

<a href="http://blog.csdn.net/u013256816/article/details/51803945">這裡有一份面筋請查收(四)</a>

<a href="http://blog.csdn.net/u013256816/article/details/51812891">這裡有一份面筋請查收(五)</a>

<a href="http://blog.csdn.net/u013256816/article/details/51823211">這裡有一份面筋請查收(六)</a>

<a href="http://blog.csdn.net/u013256816/article/details/51835169">這裡有一份面筋請查收(七)</a>

<a href="http://blog.csdn.net/u013256816/article/details/51842329">這裡有一份面筋請查收(八)</a>

參考資料