美團(tuán)后端日常實習(xí)面試,輕松拿捏了!
共 12134字,需瀏覽 25分鐘
·
2024-07-24 15:48
這是一位讀者分享的美團(tuán)日常實習(xí)面經(jīng),我對其中提到的面試問題添加了參加答案供大家復(fù)習(xí)參考。總體難度不大,但對于實習(xí)面試來說已經(jīng)算是高難度了!
合集地址:《Java 后端面經(jīng)精選(附詳細(xì)參考答案)》 。
面試情況
總時長 45 min 左右,日常實習(xí),已經(jīng)拿到 offer 了!
個人情況
-
末位 211,26 屆,學(xué)習(xí) Java 一年多一點。 -
簡歷上有一個魔改的培訓(xùn)班項目和一個參加學(xué)校比賽的項目,編碼能力一般,技術(shù)八股能力中等。
面試題
1、自我介紹。
2、拷打項目 30min ,對著項目提問架構(gòu)設(shè)計以及一些功能的開發(fā)思路。
星球里有一篇帖子分享面試官一般如何考察項目經(jīng)歷以及我們應(yīng)該如何準(zhǔn)備項目經(jīng)歷的回答:https://t.zsxq.com/136ObGpk4 。
3、看你項目用到了 CompletableFuture 進(jìn)行任務(wù)編排,怎么做的?為什么要自己創(chuàng)建一個線程池?如果有一個任務(wù)失敗了,你如何處理異常?
參考答案:從 5s 到 0.5s!看看人家的 CompletableFuture 異步任務(wù)優(yōu)化技巧,確實優(yōu)雅!。
4、 Java 線程池參數(shù)有哪些?線程數(shù)設(shè)置多少合適?線程池參數(shù)固定配置有什么問題嗎?動態(tài)線程池是什么?
參考答案:
/**
* 用給定的初始參數(shù)創(chuàng)建一個新的ThreadPoolExecutor。
*/
public ThreadPoolExecutor(int corePoolSize,//線程池的核心線程數(shù)量
int maximumPoolSize,//線程池的最大線程數(shù)
long keepAliveTime,//當(dāng)線程數(shù)大于核心線程數(shù)時,多余的空閑線程存活的最長時間
TimeUnit unit,//時間單位
BlockingQueue<Runnable> workQueue,//任務(wù)隊列,用來儲存等待執(zhí)行任務(wù)的隊列
ThreadFactory threadFactory,//線程工廠,用來創(chuàng)建線程,一般默認(rèn)即可
RejectedExecutionHandler handler//拒絕策略,當(dāng)提交的任務(wù)過多而不能及時處理時,我們可以定制策略來處理任務(wù)
) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
下面提到的公示也只是參考,實際項目不太可能直接按照公式來設(shè)置線程池參數(shù),畢竟不同的業(yè)務(wù)場景對應(yīng)的需求不同,具體還是要根據(jù)項目實際線上運行情況來動態(tài)調(diào)整。
線程數(shù)設(shè)置有一個簡單并且適用面比較廣的公式:
-
CPU 密集型任務(wù)(N+1): 這種任務(wù)消耗的主要是 CPU 資源,可以將線程數(shù)設(shè)置為 N(CPU 核心數(shù))+1。比 CPU 核心數(shù)多出來的一個線程是為了防止線程偶發(fā)的缺頁中斷,或者其它原因?qū)е碌娜蝿?wù)暫停而帶來的影響。一旦任務(wù)暫停,CPU 就會處于空閑狀態(tài),而在這種情況下多出來的一個線程就可以充分利用 CPU 的空閑時間。 -
I/O 密集型任務(wù)(2N): 這種任務(wù)應(yīng)用起來,系統(tǒng)會用大部分的時間來處理 I/O 交互,而線程在處理 I/O 的時間段內(nèi)不會占用 CPU 來處理,這時就可以將 CPU 交出給其它線程使用。因此在 I/O 密集型任務(wù)的應(yīng)用中,我們可以多配置一些線程,具體的計算方法是 2N。
線程數(shù)更嚴(yán)謹(jǐn)?shù)挠嬎愕姆椒☉?yīng)該是:最佳線程數(shù) = N(CPU 核心數(shù))?(1+WT(線程等待時間)/ST(線程計算時間)),其中 WT(線程等待時間)=線程運行總時間 - ST(線程計算時間)。
線程等待時間所占比例越高,需要越多線程。線程計算時間所占比例越高,需要越少線程。
我們可以通過 JDK 自帶的工具 VisualVM 來查看 WT/ST 比例。
-
CPU 密集型任務(wù)的 WT/ST接近或者等于 0,因此, 線程數(shù)可以設(shè)置為 N(CPU 核心數(shù))?(1+0)= N,和我們上面說的 N(CPU 核心數(shù))+1 差不多。 -
IO 密集型任務(wù)下,幾乎全是線程等待時間,從理論上來說,你就可以將線程數(shù)設(shè)置為 2N(按道理來說,WT/ST 的結(jié)果應(yīng)該比較大,這里選擇 2N 的原因應(yīng)該是為了避免創(chuàng)建過多線程吧)。
傳統(tǒng)線程池的參數(shù)(如核心線程數(shù)、最大線程數(shù)和任務(wù)隊列大小)在創(chuàng)建時被固定。隨著業(yè)務(wù)負(fù)載的變化,這種固定配置難以適應(yīng),需要調(diào)整時必須重啟服務(wù),過程繁瑣且耗時。
動態(tài)線程池是一種能夠在應(yīng)用程序運行過程中,無需重啟服務(wù)即可實時調(diào)整其核心配置參數(shù)(如核心線程數(shù)、最大線程數(shù)等)的線程池機制。通常情況下,動態(tài)線程池不僅支持參數(shù)的動態(tài)變更,還內(nèi)置了監(jiān)控和告警功能,例如在發(fā)生線程池任務(wù)堆積時通知相應(yīng)的開發(fā)人員。
5、談?wù)勀阒赖?Java 同步機制。
參考答案:
-
synchronized:Java 中的一個關(guān)鍵字,翻譯成中文是同步的意思,主要解決的是多個線程之間訪問資源的同步性,可以保證被它修飾的方法或者代碼塊在任意時刻只能有一個線程執(zhí)行。 -
ReentrantLock: 實現(xiàn)了Lock接口,是一個可重入且獨占式的鎖,和synchronized關(guān)鍵字類似。不過,ReentrantLock更靈活、更強大,增加了輪詢、超時、中斷、公平鎖和非公平鎖等高級功能。 -
ReentrantReadWriteLock:實現(xiàn)了ReadWriteLock,是一個可重入的讀寫鎖,既可以保證多個線程同時讀的效率,同時又可以保證有寫入操作時的線程安全。 -
StampedLock:JDK 1.8 引入的性能更好的讀寫鎖,不可重入且不支持條件變量Condition。不同于一般的Lock類,StampedLock并不是直接實現(xiàn)Lock或ReadWriteLock接口,而是基于 CLH 鎖 獨立實現(xiàn)的(AQS 也是基于這玩意)。 -
Semaphore:synchronized和ReentrantLock都是一次只允許一個線程訪問某個資源,而Semaphore(信號量)可以用來控制同時訪問特定資源的線程數(shù)量。 -
CountDownLatch:允許count個線程阻塞在一個地方,直至所有線程的任務(wù)都執(zhí)行完畢。CountDownLatch是一次性的,計數(shù)器的值只能在構(gòu)造方法中初始化一次,之后沒有任何機制再次對其設(shè)置值,當(dāng)CountDownLatch使用完畢后,它不能再次被使用。 -
......
6、你的項目這里為什么用分布式鎖?怎么實現(xiàn)的?
參考答案:為什么需要分布式鎖?如何基于 Redis 實現(xiàn)分布式鎖?。
7、項目用到了 Redis 哪些數(shù)據(jù)類型?
參考答案:Redis 八種常用數(shù)據(jù)類型常用命令和應(yīng)用場景。
8、MySQL 怎么解決慢查詢問題?
# 開啟慢查詢?nèi)罩竟δ?/span>
SET GLOBAL slow_query_log = 'ON';
# 慢查詢?nèi)罩敬娣盼恢?/span>
SET GLOBAL slow_query_log_file = '/var/lib/mysql/ranking-list-slow.log';
# 無論是否超時,未被索引的記錄也會記錄下來。
SET GLOBAL log_queries_not_using_indexes = 'ON';
# 慢查詢閾值(秒),SQL 執(zhí)行超過這個閾值將被記錄在日志中。
SET SESSION long_query_time = 1;
# 慢查詢僅記錄掃描行數(shù)大于此參數(shù)的 SQL
SET SESSION min_examined_row_limit = 100;
設(shè)置成功之后,使用 show variables like 'slow%'; 命令進(jìn)行查看。
| Variable_name | Value |
+---------------------+--------------------------------------+
| slow_launch_time | 2 |
| slow_query_log | ON |
| slow_query_log_file | /var/lib/mysql/ranking-list-slow.log |
+---------------------+--------------------------------------+
3 rows in set (0.01 sec)
我們故意在百萬數(shù)據(jù)量的表(未使用索引)中執(zhí)行一條排序的語句:
SELECT `score`,`name` FROM `cus_order` ORDER BY `score` DESC;
確保自己有對應(yīng)目錄的訪問權(quán)限:
chmod 755 /var/lib/mysql/
查看對應(yīng)的慢查詢?nèi)罩荆?/p>
cat /var/lib/mysql/ranking-list-slow.log
我們剛剛故意執(zhí)行的 SQL 語句已經(jīng)被慢查詢?nèi)罩居涗浟讼聛恚?/p>
# Time: 2022-10-09T08:55:37.486797Z
# User@Host: root[root] @ [172.17.0.1] Id: 14
# Query_time: 0.978054 Lock_time: 0.000164 Rows_sent: 999999 Rows_examined: 1999998
SET timestamp=1665305736;
SELECT `score`,`name` FROM `cus_order` ORDER BY `score` DESC;
這里對日志中的一些信息進(jìn)行說明:
-
Time:被日志記錄的代碼在服務(wù)器上的運行時間。 -
User@Host:誰執(zhí)行的這段代碼。 -
Query_time:這段代碼運行時長。 -
Lock_time:執(zhí)行這段代碼時,鎖定了多久。 -
Rows_sent:慢查詢返回的記錄。 -
Rows_examined:慢查詢掃描過的行數(shù)。
實際項目中,慢查詢?nèi)罩就ǔ容^復(fù)雜,我們需要借助一些工具對其進(jìn)行分析。像 MySQL 內(nèi)置的 mysqldumpslow 工具就可以把相同的 SQL 歸為一類,并統(tǒng)計出歸類項的執(zhí)行次數(shù)和每次執(zhí)行的耗時等一系列對應(yīng)的情況。
找到了慢 SQL 之后,我們可以通過 EXPLAIN 命令分析對應(yīng)的 SELECT 語句:
mysql> EXPLAIN SELECT `score`,`name` FROM `cus_order` ORDER BY `score` DESC;
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
| 1 | SIMPLE | cus_order | NULL | ALL | NULL | NULL | NULL | NULL | 997572 | 100.00 | Using filesort |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
1 row in set, 1 warning (0.00 sec)
比較重要的字段說明:
-
select_type:查詢的類型,常用的取值有 SIMPLE(普通查詢,即沒有聯(lián)合查詢、子查詢)、PRIMARY(主查詢)、UNION(UNION 中后面的查詢)、SUBQUERY(子查詢)等。 -
table:表示查詢涉及的表或衍生表。 -
type:執(zhí)行方式,判斷查詢是否高效的重要參考指標(biāo),結(jié)果值從差到好依次是:ALL < index < range ~ index_merge < ref < eq_ref < const < system。 -
rows: SQL 要查找到結(jié)果集需要掃描讀取的數(shù)據(jù)行數(shù),原則上 rows 越少越好。 -
......
關(guān)于 Explain 的詳細(xì)介紹,請看這篇文章:MySQL 執(zhí)行計劃分析[1]。另外,再推薦一下阿里的這篇文章:慢 SQL 治理經(jīng)驗總結(jié),總結(jié)的挺不錯。
9、MySQL 的隔離級別是基于鎖實現(xiàn)的嗎?
MySQL 的隔離級別基于鎖和 MVCC 機制共同實現(xiàn)的。
SERIALIZABLE 隔離級別是通過鎖來實現(xiàn)的,READ-COMMITTED 和 REPEATABLE-READ 隔離級別是基于 MVCC 實現(xiàn)的。不過, SERIALIZABLE 之外的其他隔離級別可能也需要用到鎖機制,就比如 REPEATABLE-READ 在當(dāng)前讀情況下需要使用加鎖讀來保證不會出現(xiàn)幻讀。
10、MySQL 的默認(rèn)隔離級別是什么?可以解決幻讀問題問題嗎?
MySQL InnoDB 存儲引擎的默認(rèn)支持的隔離級別是 REPEATABLE-READ(可重讀)。我們可以通過SELECT @@tx_isolation;命令來查看,MySQL 8.0 該命令改為SELECT @@transaction_isolation;
mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
標(biāo)準(zhǔn)的 SQL 隔離級別定義里,REPEATABLE-READ(可重復(fù)讀)是不可以防止幻讀的。
但是!InnoDB 實現(xiàn)的 REPEATABLE-READ 隔離級別其實是可以解決幻讀問題發(fā)生的,主要有下面兩種情況:
-
快照讀:由 MVCC 機制來保證不出現(xiàn)幻讀。 -
當(dāng)前讀:使用 Next-Key Lock 進(jìn)行加鎖來保證不出現(xiàn)幻讀,Next-Key Lock 是行鎖(Record Lock)和間隙鎖(Gap Lock)的結(jié)合,行鎖只能鎖住已經(jīng)存在的行,為了避免插入新行,需要依賴間隙鎖。
因為隔離級別越低,事務(wù)請求的鎖越少,所以大部分?jǐn)?shù)據(jù)庫系統(tǒng)的隔離級別都是 READ-COMMITTED ,但是你要知道的是 InnoDB 存儲引擎默認(rèn)使用 REPEATABLE-READ 并不會有任何性能損失。
11、算法:買賣股票的最佳時機 II
Leetcode 原題:122.買賣股票的最佳時機 II[2] 。
MySQL 執(zhí)行計劃分析: https://javaguide.cn/database/mysql/mysql-query-execution-plan.html
[2]122.買賣股票的最佳時機 II: https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/
歡迎準(zhǔn)備面試的同學(xué)加入我的知識星球 。
星球部分面試資料介紹:
-
《Java 面試指北》(面試專版,Java面試必備) -
《后端面試高頻系統(tǒng)設(shè)計&場景題》(20+高頻系統(tǒng)設(shè)計&場景面試題) -
《Java 必讀源碼系列》(目前已經(jīng)整理了 Dubbo 2.6.x 、Netty 4.x、SpringBoot2.1 的源碼) -
《后端高頻筆試題(非常規(guī)Leetcode類型)》(新增了多線程相關(guān)的手撕題) -
《Java 面試常見問題總結(jié)(2024 最新版)》(350+ 道超高頻面試題總結(jié)) -
20 道 HR 面的常見問題(HR 面必備)
2024 最新版原創(chuàng) PDF 面試資料來啦!涵蓋 Java 核心、數(shù)據(jù)庫、緩存、分布式、設(shè)計模式、智力題等內(nèi)容,非常全面!
