小編的世界 優質文選 資料
字體大小:
2021年6月03日 -
:
Java領路人
最近在面試大廠,想通過百家號記錄一下我面試過程與問題,與大家分享。答案是自己整理的,如有什麼問題歡迎隨時交流~~
打怪升級九九八十一難之阿裏一面,面試方式:電話面試(視頻面試一般會手撕算法,電話面試還好,只需大聲背誦八股文)
面試官:您好,我這邊是阿裏******部,請問還在看機會嗎
我:您好,在看,自我介紹,巴拉巴拉,做過的項目巴拉巴拉.
然後是技術面試,以下是面試題(面試我會錄音所以能都記錄下來):
1.項目中最近遇到的難點,如何解決的?
業務場景(此業務用戶數約幾十萬):
之前扣款為實時扣款,即用戶消費後實時扣費
,幾乎沒有一個賬戶同時多條扣款 即多線程扣款的情況。之前的解決方案為:
在賬戶表中增加版本號字段,每次查詢餘額時會查出版本號,計算金額後事務更新賬戶表,插入流水表時,where version_id = 查詢時版本號,不等於的話,說明餘額已被修改,則更新失敗,其實就是一個樂觀鎖
。
這時樂觀鎖完全夠用,因為一個時間戳最多一筆扣款。
此時業務組改造 傳過來的扣款變為 實時賬單扣款+賬單日扣款:
賬單日一天為一筆扣款,用戶會有賬單積壓情況,如果一個月沒扣的話,就是30筆訂單。
此時業務組會同時發過來30筆賬單,此時就有問題了,由於樂觀鎖控制,賬戶錢足夠時,30筆賬單同時扣款大多數都會失敗。
改造方法:
方案一:數據庫行鎖
方案二:redis分布式鎖
方案一:
之前由於業務邏輯需要,在事務外側進行查詢,然後將數據傳入事務,在事務中計算金額、版本號。
改造後,改為事務中再次查詢賬戶 通過for update增加行鎖,寫鎖將賬戶鎖住,此時其他線程for update查詢時會被堵塞,直到第一個線程修改完成,事務提交後釋放行鎖。
方案二:
在事務外查詢餘額前增加redis分布式鎖。一定在查詢前加,否則會導致金額計算錯誤的情況。此種方案,事務中不用二次查詢賬戶。
兩種方案比較:
兩個都是堵塞線程。個人感覺redis更好一下,一方面減少了事務中二次查詢,另一方面不會占用數據庫連接池連接。
2.聊一下hashmap?hashmsp線程安全嗎? 八股文開整
這個不寫了,網上很多。
3.項目中用到的設計模式?
工廠模式(扣款、退款時根據渠道獲取不同銀行的service)、策略模式(賬戶扣費時,不同的交易場景不同的算法)、代理模式(service前後日志打印、校驗等)、發布訂閱模式(訂單結果同時通知多個組時,類似於公眾號發布後通知各個用戶)
4.Jdk動態代理與cglib動態代理的區別?
jdk動態代理:
1.jdk代理存在的問題在於目標類被代理的方法必須實現某個接口。
代理類與目標類都會實現同一個接口,並在代理類中會反射調用目標類中方法,調用者實際調用的則是代理類的方法,通過這種方式我們可以在代理類中織入切面邏輯。
2.代理對象需要繼承invocationHandler,代理類調用方法時會調用invocationHandler的invoke方法,proxy是所有代理類的父類,通過newProxyInstance動態創建代理對象。
3.代理對象在運行期創建,可以將其寫入磁盤。動態代理對象proxy0繼承了proxy,並實現了被代理類實現的接口,由於java是單繼承,動態代理對象默認繼承了proxy,所以jdk動態代理基於接口實現。代理類通過反射調用被代理的方法。
4.擴展下,jdk動態代理中,目標方法調用自己類中另一個方法,會經過代理對象嗎? 不會。因為內部調用this.method,這個this指的是被代理類本身,而不是代理對象。
所以例如@Transcation、@Sync等注解是通過aop實現,但是方法內部調用本類中其他帶有該注解的方法時,是失效的。
cglib動態代理:
1.cglib動態代理通過ASM進行字節碼增強,然後生成一個目標類的子類,通過在子類中織入相應邏輯達到代理邏輯。
調用方法就直接調用,不需要再通過反射調用。
2.cglib代理的目標類調用自己的另一個方法會經過代理對象,因為底層不像JDK動態代理的反射方式調用,而是通過動態生成子類的方式調用。
3.CGLib動態代理,是基於繼承目標類來實現代理,所以無法對final類、private方法和static方法進行代理。
5.AOP原理?
Aop面向切面編程是Spring核心組件之一。是對OOP(面向對象編程)的補充。面向對象編程中,關鍵單元是對象,AOP的關鍵單元是切面,在實際邏輯執行之前、之後添加關注點。這讓代碼在當下和將來都變得易於維護。
使用場景? 前後日志打印、訂單校驗、@transcation/@Sync等
Spring aop中不同的通知類型?
前置通知 在連接點之前執行。
返回之後通知 在連接點正常結束之後執行的advice。
拋出異常後執行通知 如果一個方法拋出異常來退出的話,這個advice就會被執行。
後置通知 無論連接點已什麼方式退出,都會執行。
環繞通知 圍繞連接點執行
6.rabbbitmp中push和pull,用的哪一種,區別是什麼?
rabbitmq與rocketmq都支持push和pull,kafka只支持pull。
push優點:
服務端主動推給客戶端,及時性很高,幾乎實時
push缺點:
當客戶端消費能力遠低於服務端生產能力,一旦服務端推送大量消息到客戶端時,就會導致客戶端消息堆積,處理緩慢,甚至服務崩潰。(如何解決? Mq提供流控制,也就是依據客戶端消費能力做流控,比如rabbitmq限制消費數量)
服務端需要維護每次傳輸的狀態,以防消息傳遞失敗進行重試。
Pull優點:
客戶端可以根據自己消費能力進行消費
pull缺點:
主動到服務端拉取消息。拉取消息的間隔不太好設置,間隔太短對服務器請求壓力太大。間隔時間過長,會造成一部分數據的延遲。
7.內存溢出與內存泄漏?
溢出:
系統已經不能再分配所需要的空間,比如需要100M,系統只剩90M,這就是內存溢出
泄漏:
已分配的對象空間,用完後沒有被釋放。
內存一直被占用,多次內存泄漏會導致內存溢出。
8.對springboot的理解?
約定大於配置
9.線程池各個參數?
corePoolsize:核心線程數。
當有任務到來時會創建核心線程,當達到corePoolsize時且未達到最大線程數,仍然有任務時,將會繼續創建非核心線程。如果核心線程數等於最大線程數,核心線程都被激活,但仍然有新任務時,任務將會被掛起。
maximumPoolSize:最大線程數。如果最大線程數量等於核心線程數,則無法創建非核心線程;如果非核心線程處於空閑時,超過設置的空閑時間,則將被回收,釋放占用的資源。
keepAliveTime:當線程空閑時,所允許保存的最大時間,超過這個時間,線程將被釋放銷毀,只針對非核心線程。
util:時間單位,TimeUnit.SECONDS等
workQueue:任務隊列,存儲暫時無法執行的任務,等待空閑線程來執行任務,workQueue下又包括SynchronousQueue、LinkedBlockingQueue、ArrayBlockingQueue等
threadFactory:線程工程,用於創建線程
handler:當線程邊界和隊列容量已經達到最大時,用於處理阻塞時的程序。默認有5種策略:
AbortPolicy
該策略是線程池的默認策略。使用該策略時,如果線程池隊列滿了丟掉這個任務並且拋出RejectedExecutionException異常。
DiscardPolicy
這個策略和AbortPolicy的slient版本,如果線程池隊列滿了,會直接丟掉這個任務並且不會有任何異常
DiscardOldestPolicy
這個策略從字面上也很好理解,丟棄最老的。也就是說如果隊列滿了,會將最早進入隊列的任務刪掉騰出空間,再嘗試加入隊列。
CallerRunsPolicy
使用此策略,如果添加到線程池失敗,那麼主線程會自己去執行該任務,不會等待線程池中的線程去執行。就像是個急脾氣的人,我等不到別人來做這件事就幹脆自己幹。
10.事務ACID?mysql髒讀、幻讀、不可重複讀?
一、原子性(atomicity)
一個事務要麼全部提交成功,要麼全部失敗回滾,不能只執行其中的一部分操作,這就是事務的原子性
二、一致性(consistency)
事務的執行不能破壞數據庫數據的完整性和一致性,一個事務在執行之前和執行之後,數據庫都必須處於一致性狀態。
如果數據庫系統在運行過程中發生故障,有些事務尚未完成就被迫中斷,這些未完成的事務對數據庫所作的修改有一部分已寫入物理數據庫,這是數據庫就處於一種不正確的狀態,也就是不一致的狀態
三、隔離性(isolation)
事務互相隔離
四、持久性(durability)
一旦事務提交,那麼它對數據庫中的對應數據的狀態的變更就會永久保存到數據庫中。--即使發生系統崩潰或機器宕機等故障,只要數據庫能夠重新啟動,那麼一定能夠將其恢複到事務成功結束的狀態
髒讀:一個事務讀到了另一個事務未提交的數據
不可重複讀:一個事務在讀取數據一段時間後,再次讀取這個數據,發現讀取的數據發生了改變(被修改或刪除)
幻讀:一個事務按照相同的條件檢索,發現結果集變多或者變少(由其他事務插入或刪除),產生了幻覺
還有更多Java面試資料就不一一分享出來了,太多了都是精華面試題,有需要的朋友可以私信我喲~~~