您好,登錄后才能下訂單哦!
看到分布式、高并發(fā)、多線程這三個(gè)詞的時(shí)候,很多人是不是都認(rèn)為分布式=高并發(fā)=多線程?
當(dāng)面試官問到高并發(fā)系統(tǒng)可以采用哪些手段來解決,或者被問到分布式系統(tǒng)如何解決一致性的問題,是不是一臉懵逼?
確實(shí),在一開始接觸的時(shí)候,不少人都會分布式、高并發(fā)、多線程將三者混淆,誤以為所謂的分布式高并發(fā)的系統(tǒng)就是能同時(shí)供海量用戶訪問,而采用多線程手段不就是可以提供系統(tǒng)的并發(fā)能力嗎?實(shí)際上,他們?nèi)齻€(gè)總是相伴而生,但側(cè)重點(diǎn)又有不同。
接下來我就看看分布式、高并發(fā)、多線程這三者之間到底有什么區(qū)別?
分布式更多的一個(gè)概念,是為了解決單個(gè)物理服務(wù)器容量和性能瓶頸問題而采用的優(yōu)化手段。該領(lǐng)域需要解決的問題極多,在不同的技術(shù)層面上,又包括:分布式文件系統(tǒng)、分布式緩存、分布式數(shù)據(jù)庫、分布式計(jì)算等,一些名詞如Hadoop、zookeeper、MQ等都跟分布式有關(guān)。從理念上講,分布式的實(shí)現(xiàn)有兩種形式:
水平擴(kuò)展:當(dāng)一臺機(jī)器扛不住流量時(shí),就通過添加機(jī)器的方式,將流量平分到所有服務(wù)器上,所有機(jī)器都可以提供相當(dāng)?shù)姆?wù);
垂直拆分:前端有多種查詢需求時(shí),一臺機(jī)器扛不住,可以將不同的需求分發(fā)到不同的機(jī)器上,比如A機(jī)器處理余票查詢的請求,B機(jī)器處理支付的請求。
相對于分布式來講,高并發(fā)在解決的問題上會集中一些,其反應(yīng)的是同時(shí)有多少量:比如在線直播服務(wù),同時(shí)有上萬人觀看。
高并發(fā)可以通過分布式技術(shù)去解決,將并發(fā)流量分到不同的物理服務(wù)器上。但除此之外,還可以有很多其他優(yōu)化手段:比如使用緩存系統(tǒng),將所有的,靜態(tài)內(nèi)容放到CDN等;還可以使用多線程技術(shù)將一臺服務(wù)器的服務(wù)能力最大化。
多線程是指從軟件或者硬件上實(shí)現(xiàn)多個(gè)線程并發(fā)執(zhí)行的技術(shù),它更多的是解決CPU調(diào)度多個(gè)進(jìn)程的問題,從而讓這些進(jìn)程看上去是同時(shí)執(zhí)行(實(shí)際是交替運(yùn)行的)。
這幾個(gè)概念中,多線程解決的問題是最明確的,手段也是比較單一的,基本上遇到的最大問題就是線程安全。在JAVA語言中,需要對JVM內(nèi)存模型、指令重排等深入了解,才能寫出一份高質(zhì)量的多線程代碼。
總結(jié)一下:
分布式是從物理資源的角度去將不同的機(jī)器組成一個(gè)整體對外服務(wù),技術(shù)范圍非常廣且難度非常大,有了這個(gè)基礎(chǔ),高并發(fā)、高吞吐等系統(tǒng)很容易構(gòu)建;
高并發(fā)是從業(yè)務(wù)角度去描述系統(tǒng)的能力,實(shí)現(xiàn)高并發(fā)的手段可以采用分布式,也可以采用諸如緩存、CDN等,當(dāng)然也包括多線程;
多線程則聚焦于如何使用編程語言將CPU調(diào)度能力最大化。
下面給大家分享一些面試官常問的分布式、高并發(fā)、多線程的面試題
1、分布式系統(tǒng)怎么做服務(wù)治理
針對互聯(lián)網(wǎng)業(yè)務(wù)的特點(diǎn),eg 突發(fā)的流量高峰、網(wǎng)絡(luò)延時(shí)、機(jī)房故障等,重點(diǎn)針對大規(guī)??鐧C(jī)房的海量服務(wù)進(jìn)行運(yùn)行態(tài)治理,保障線上服務(wù)的高SLA,滿足用戶的體驗(yàn),常用的策略包括限流降級、服務(wù)嵌入遷出、服務(wù)動態(tài)路由和灰度發(fā)布等
2、對分布式事務(wù)的理解
本質(zhì)上來說,分布式事務(wù)就是為了保證不同數(shù)據(jù)庫的數(shù)據(jù)一致性。
事務(wù)的ACID特性 原子性 一致性 隔離性 持久性
消息事務(wù)+最終一致性
CC提供了一個(gè)編程框架,將整個(gè)業(yè)務(wù)邏輯分為三塊:Try、Confirm和Cancel三個(gè)操作。以在線下單為例,Try階段會去扣庫存,Confirm階段則是去更新訂單狀態(tài),如果更新訂單失敗,則進(jìn)入Cancel階段,會去恢復(fù)庫存??傊?,TCC就是通過代碼人為實(shí)現(xiàn)了兩階段提交,不同的業(yè)務(wù)場景所寫的代碼都不一樣,復(fù)雜度也不一樣,因此,這種模式并不能很好地被復(fù)用。
3、如何實(shí)現(xiàn)負(fù)載均衡,有哪些算法可以實(shí)現(xiàn)?
經(jīng)常會用到以下四種算法:隨機(jī)(random)、輪訓(xùn)(round-robin)、一致哈希(consistent-hash)和主備(master-slave)。
4、分布式集群下如何做到唯一序列號
Redis生成ID 這主要依賴于Redis是單線程的,所以也可以用生成全局唯一的ID。可以用Redis的原子操作 INCR和INCRBY來實(shí)現(xiàn)。
5. 什么是進(jìn)程
進(jìn)程是指運(yùn)行中的應(yīng)用程序,每個(gè)進(jìn)程都有自己獨(dú)立的地址空間(內(nèi)存空間)。
比如用戶點(diǎn)擊桌面的IE瀏覽器,就啟動了一個(gè)進(jìn)程,操作系統(tǒng)就會為該進(jìn)程分配獨(dú)立的地址空間。當(dāng)用戶再次點(diǎn)擊左邊的IE瀏覽器,又啟動了一個(gè)進(jìn)程,操作系統(tǒng)將為新的進(jìn)程分配新的獨(dú)立的地址空間。目前操作系統(tǒng)都支持多進(jìn)程。
6. 什么是線程
進(jìn)程是表示自愿分配的基本單位。而線程則是進(jìn)程中執(zhí)行運(yùn)算的最小單位,即執(zhí)行處理機(jī)調(diào)度的基本單位。通俗來講:一個(gè)程序有一個(gè)進(jìn)程,而一個(gè)進(jìn)程可以有多個(gè)線程。
7. 線程和進(jìn)程有什么區(qū)別
線程是進(jìn)程的子集,一個(gè)進(jìn)程可以有很多線程,每條線程并行執(zhí)行不同的任務(wù)。不同的進(jìn)程使用不同的內(nèi)存空間,而所有的線程共享一片相同的內(nèi)存空間。
8. 多線程的幾種實(shí)現(xiàn)方式
(1) 繼承Thread類創(chuàng)建線程
Thread類本質(zhì)上是實(shí)現(xiàn)了Runnable接口的一個(gè)實(shí)例,代表一個(gè)線程的實(shí)例。啟動線程的唯一方法就是通過Thread類的start()實(shí)例方法。start()方法將啟動一個(gè)新線程,并執(zhí)行run()方法。這種方式實(shí)現(xiàn)多線程比較簡單,通過自己的類直接繼承Thread,并重寫run()方法,就可以啟動新線程并執(zhí)行自己定義的run()方法。
(2) 實(shí)現(xiàn)Runnable接口創(chuàng)建線程
如果自己的類已經(jīng)繼承了兩一個(gè)類,就無法再繼承Thread,因此可以實(shí)現(xiàn)一個(gè)Runnable接口
(3) 實(shí)現(xiàn)Callable接口通過FutureTask包裝器來創(chuàng)建Thread線程
(4) 使用ExecutorService、Callable、Future實(shí)現(xiàn)有返回結(jié)果的線程
ExecutorService、Callable、Future三個(gè)接口實(shí)際上都是屬于Executor框架。返回結(jié)果的線程是在JDK1.5中引入的新特征,有了這種特征就不需要再為了得到返回值而大費(fèi)周折了。
可返回值的任務(wù)必須實(shí)現(xiàn)Callable接口;無返回值的任務(wù)必須實(shí)現(xiàn)Runnabel接口。
執(zhí)行Callable任務(wù)后,可以獲取一個(gè)Future對象,在該對象上調(diào)用get()方法就可以獲取到Callable任務(wù)返回的Object了。(get()方法是阻塞的,線程無返回結(jié)果,該方法就一直等待)
9. 多線程中忙循環(huán)是什么
忙循環(huán)就是程序員用循環(huán)讓一個(gè)線程等待,不像傳統(tǒng)方法wait()、sleep()或者yied()它們都放棄了CPU控制,而忙循環(huán)不會放棄CPU,它就是在運(yùn)行一個(gè)空循環(huán)。這么做的目的是為了保留CPU緩存,在多核系統(tǒng)中,一個(gè)等待線程醒來的時(shí)候可能會在另一個(gè)內(nèi)核運(yùn)行,這樣會重建緩存。為了避免重建緩存和減少等待重建的時(shí)間就可以使用它了。
10. 什么是java內(nèi)存模型
java內(nèi)存模型定義了java虛擬機(jī)在計(jì)算機(jī)內(nèi)存中的工作方式。JMM決定了一個(gè)線程對共享變量的寫入何時(shí)對另一個(gè)線程可見。從抽象的角度來看,JMM定義了線程和主內(nèi)存之間的抽象關(guān)系:線程之間的共享變量存儲在主內(nèi)存中,每一個(gè)線程都有一個(gè)私有的本地內(nèi)存,本地內(nèi)存中存儲了該線程以讀/寫共享變量的副本。
11. 為什么要用線程池?
線程池提供了一種限制和管理資源(包括執(zhí)行一個(gè)任務(wù))。 每個(gè)線程池還維護(hù)一些基本統(tǒng)計(jì)信息,例如已完成任務(wù)的數(shù)量。
這里借用《Java并發(fā)編程的藝術(shù)》提到的來說一下使用線程池的好處:
降低資源消耗。 通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
提高響應(yīng)速度。 當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行。
提高線程的可管理性。 線程是稀缺資源,如果無限制的創(chuàng)建,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。
12、什么是樂觀鎖和悲觀鎖
1)樂觀鎖:就像它的名字一樣,對于并發(fā)間操作產(chǎn)生的線程安全問題持樂觀狀態(tài),樂觀鎖認(rèn)為競爭不總是會發(fā)生,因此它不需要持有鎖,將比較-替換這兩個(gè)動作作為一個(gè)原子操作嘗試去修改內(nèi)存中的變量,如果失敗則表示發(fā)生沖突,那么就應(yīng)該有相應(yīng)的重試邏輯。
2)悲觀鎖:還是像它的名字一樣,對于并發(fā)間操作產(chǎn)生的線程安全問題持悲觀狀態(tài),悲觀鎖認(rèn)為競爭總是會發(fā)生,因此每次對某資源進(jìn)行操作時(shí),都會持有一個(gè)獨(dú)占的鎖,就像synchronized,不管三七二十一,直接上了鎖就操作資源了。
13、高并發(fā)、任務(wù)執(zhí)行時(shí)間短的業(yè)務(wù)怎樣使用線程池?并發(fā)不高、任務(wù)執(zhí)行時(shí)間長的業(yè)務(wù)怎樣使用線程池?并發(fā)高、業(yè)務(wù)執(zhí)行時(shí)間長的業(yè)務(wù)怎樣使用線程池?
1)高并發(fā)、任務(wù)執(zhí)行時(shí)間短的業(yè)務(wù),線程池線程數(shù)可以設(shè)置為CPU核數(shù)+1,減少線程上下文的切換
2)并發(fā)不高、任務(wù)執(zhí)行時(shí)間長的業(yè)務(wù)要區(qū)分開看:
a)假如是業(yè)務(wù)時(shí)間長集中在IO操作上,也就是IO密集型的任務(wù),因?yàn)镮O操作并不占用CPU,所以不要讓所有的CPU閑下來,可以加大線程池中的線程數(shù)目,讓CPU處理更多的業(yè)務(wù)
b)假如是業(yè)務(wù)時(shí)間長集中在計(jì)算操作上,也就是計(jì)算密集型任務(wù),這個(gè)就沒辦法了,和(1)一樣吧,線程池中的線程數(shù)設(shè)置得少一些,減少線程上下文的切換
c)并發(fā)高、業(yè)務(wù)執(zhí)行時(shí)間長,解決這種類型任務(wù)的關(guān)鍵不在于線程池而在于整體架構(gòu)的設(shè)計(jì),看看這些業(yè)務(wù)里面某些數(shù)據(jù)是否能做緩存是第一步,增加服務(wù)器是第二步,至于線程池的設(shè)置,設(shè)置參考其他有關(guān)線程池的文章。最后,業(yè)務(wù)執(zhí)行時(shí)間長的問題,也可能需要分析一下,看看能不能使用中間件對任務(wù)進(jìn)行拆分和解耦。
歡迎大家一起交流,喜歡文章記得點(diǎn)個(gè)贊,感謝支持!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。