科大訊飛薪資一開,性價比不要太高!
科大訊飛一面
sycronized和locked的區(qū)別?
Synchronized編碼更簡單,鎖機制由JVM維護,在競爭不激烈的情況下性能更好。Lock功能更強大更靈活,競爭激烈時性能較好。
區(qū)別如下:
- 來源:lock是一個接口,而synchronized是java的一個關(guān)鍵字,synchronized是內(nèi)置的語言實現(xiàn);
- 異常是否釋放鎖:synchronized在發(fā)生異常時候會自動釋放占有的鎖,因此不會出現(xiàn)死鎖;而lock發(fā)生異常時候,不會主動釋放占有的鎖,必須手動unlock來釋放鎖,可能引起死鎖的發(fā)生。(所以最好將同步代碼塊用try catch包起來,finally中寫入unlock,避免死鎖的發(fā)生。)
- 是否響應(yīng)中斷l(xiāng)ock等待鎖過程中可以用interrupt來中斷等待,而synchronized只能等待鎖的釋放,不能響應(yīng)中斷;
- 是否知道獲取鎖:Lock可以通過trylock來知道有沒有獲取鎖,而synchronized不能;
- Lock可以提高多個線程進行讀操作的效率。(可以通過readwritelock實現(xiàn)讀寫分離)
- 在性能上來說,如果競爭資源不激烈,兩者的性能是差不多的,而當競爭資源非常激烈時(即有大量線程同時競爭),此時Lock的性能要遠遠優(yōu)于synchronized。
hashmap put的流程
圖片
HashMap HashMap的put()方法用于向HashMap中添加鍵值對,當調(diào)用HashMap的put()方法時,會按照以下詳細流程執(zhí)行(JDK8 1.8版本):
第一步:根據(jù)要添加的鍵的哈希碼計算在數(shù)組中的位置(索引)。
第二步:檢查該位置是否為空(即沒有鍵值對存在)
- 如果為空,則直接在該位置創(chuàng)建一個新的Entry對象來存儲鍵值對。將要添加的鍵值對作為該Entry的鍵和值,并保存在數(shù)組的對應(yīng)位置。將HashMap的修改次數(shù)(modCount)加1,以便在進行迭代時發(fā)現(xiàn)并發(fā)修改。
第三步:如果該位置已經(jīng)存在其他鍵值對,檢查該位置的第一個鍵值對的哈希碼和鍵是否與要添加的鍵值對相同?
- 如果相同,則表示找到了相同的鍵,直接將新的值替換舊的值,完成更新操作。
第四步:如果第一個鍵值對的哈希碼和鍵不相同,則需要遍歷鏈表或紅黑樹來查找是否有相同的鍵:
如果鍵值對集合是鏈表結(jié)構(gòu),從鏈表的頭部開始逐個比較鍵的哈希碼和equals()方法,直到找到相同的鍵或達到鏈表末尾。
- 如果找到了相同的鍵,則使用新的值取代舊的值,即更新鍵對應(yīng)的值。
- 如果沒有找到相同的鍵,則將新的鍵值對添加到鏈表的頭部。
如果鍵值對集合是紅黑樹結(jié)構(gòu),在紅黑樹中使用哈希碼和equals()方法進行查找。根據(jù)鍵的哈希碼,定位到紅黑樹中的某個節(jié)點,然后逐個比較鍵,直到找到相同的鍵或達到紅黑樹末尾。
- 如果找到了相同的鍵,則使用新的值取代舊的值,即更新鍵對應(yīng)的值。
- 如果沒有找到相同的鍵,則將新的鍵值對添加到紅黑樹中。
第五步:檢查鏈表長度是否達到閾值(默認為8):
- 如果鏈表長度超過閾值,且HashMap的數(shù)組長度大于等于64,則會將鏈表轉(zhuǎn)換為紅黑樹,以提高查詢效率。
第六步:檢查負載因子是否超過閾值(默認為0.75):
- 如果鍵值對的數(shù)量(size)與數(shù)組的長度的比值大于閾值,則需要進行擴容操作。
第七步:擴容操作:
- 創(chuàng)建一個新的兩倍大小的數(shù)組。
- 將舊數(shù)組中的鍵值對重新計算哈希碼并分配到新數(shù)組中的位置。
- 更新HashMap的數(shù)組引用和閾值參數(shù)。
第八步:完成添加操作。
此外,HashMap是非線程安全的,如果在多線程環(huán)境下使用,需要采取額外的同步措施或使用線程安全的ConcurrentHashMap。
avl樹和紅黑樹的區(qū)別?
- 平衡二叉樹追求的是一種 “完全平衡” 狀態(tài):任何結(jié)點的左右子樹的高度差不會超過 1,優(yōu)勢是樹的結(jié)點是很平均分配的。這個要求實在是太嚴了,導(dǎo)致每次進行插入/刪除節(jié)點的時候,幾乎都會破壞平衡樹的第二個規(guī)則,進而我們都需要通過左旋和右旋來進行調(diào)整,使之再次成為一顆符合要求的平衡樹。
- 紅黑樹不追求這種完全平衡狀態(tài),而是追求一種 “弱平衡” 狀態(tài):整個樹最長路徑不會超過最短路徑的 2 倍。優(yōu)勢是雖然犧牲了一部分查找的性能效率,但是能夠換取一部分維持樹平衡狀態(tài)的成本。與平衡樹不同的是,紅黑樹在插入、刪除等操作,不會像平衡樹那樣,頻繁著破壞紅黑樹的規(guī)則,所以不需要頻繁著調(diào)整,這也是我們?yōu)槭裁创蠖鄶?shù)情況下使用紅黑樹的原因。
紅黑樹插入的時間復(fù)雜度是多少?
紅黑樹平衡,插入、刪除、查找操作的時間復(fù)雜度都是O(logn)。
如何給數(shù)據(jù)庫中數(shù)據(jù)加行級鎖?
InnoDB 引擎是支持行級鎖的,而 MyISAM 引擎并不支持行級鎖。
普通的 select 語句是不會對記錄加鎖的,因為它屬于快照讀。如果要在查詢時對記錄加行鎖,可以使用下面這兩個方式,這種查詢會加鎖的語句稱為鎖定讀。
//對讀取的記錄加共享鎖
select ... lock in share mode;
//對讀取的記錄加獨占鎖
select ... for update;
上面這兩條語句必須在一個事務(wù)中,因為當事務(wù)提交了,鎖就會被釋放,所以在使用這兩條語句的時候,要加上 begin、start transaction 或者 set autocommit = 0。
共享鎖(S鎖)滿足讀讀共享,讀寫互斥。獨占鎖(X鎖)滿足寫寫互斥、讀寫互斥。
圖片
行級鎖的類型主要有三類:
- Record Lock,記錄鎖,也就是僅僅把一條記錄鎖上;
- Gap Lock,間隙鎖,鎖定一個范圍,但是不包含記錄本身;
- Next-Key Lock:Record Lock + Gap Lock 的組合,鎖定一個范圍,并且鎖定記錄本身。
Record Lock
Record Lock 稱為記錄鎖,鎖住的是一條記錄。而且記錄鎖是有 S 鎖和 X 鎖之分的:
- 當一個事務(wù)對一條記錄加了 S 型記錄鎖后,其他事務(wù)也可以繼續(xù)對該記錄加 S 型記錄鎖(S 型與 S 鎖兼容),但是不可以對該記錄加 X 型記錄鎖(S 型與 X 鎖不兼容);
- 當一個事務(wù)對一條記錄加了 X 型記錄鎖后,其他事務(wù)既不可以對該記錄加 S 型記錄鎖(S 型與 X 鎖不兼容),也不可以對該記錄加 X 型記錄鎖(X 型與 X 鎖不兼容)。
舉個例子,當一個事務(wù)執(zhí)行了下面這條語句:
mysql > begin;
mysql > select * from t_test where id = 1 for update;
就是對 t_test 表中主鍵 id 為 1 的這條記錄加上 X 型的記錄鎖,這樣其他事務(wù)就無法對這條記錄進行修改了。
圖片
當事務(wù)執(zhí)行 commit 后,事務(wù)過程中生成的鎖都會被釋放。
Gap Lock
Gap Lock 稱為間隙鎖,只存在于可重復(fù)讀隔離級別,目的是為了解決可重復(fù)讀隔離級別下幻讀的現(xiàn)象。
假設(shè),表中有一個范圍 id 為(3,5)間隙鎖,那么其他事務(wù)就無法插入 id = 4 這條記錄了,這樣就有效的防止幻讀現(xiàn)象的發(fā)生。
圖片
間隙鎖雖然存在 X 型間隙鎖和 S 型間隙鎖,但是并沒有什么區(qū)別,間隙鎖之間是兼容的,即兩個事務(wù)可以同時持有包含共同間隙范圍的間隙鎖,并不存在互斥關(guān)系,因為間隙鎖的目的是防止插入幻影記錄而提出的。
Next-Key Lock
Next-Key Lock 稱為臨鍵鎖,是 Record Lock + Gap Lock 的組合,鎖定一個范圍,并且鎖定記錄本身。
假設(shè),表中有一個范圍 id 為(3,5] 的 next-key lock,那么其他事務(wù)即不能插入 id = 4 記錄,也不能修改 id = 5 這條記錄。
圖片
所以,next-key lock 即能保護該記錄,又能阻止其他事務(wù)將新紀錄插入到被保護記錄前面的間隙中。
next-key lock 是包含間隙鎖+記錄鎖的,如果一個事務(wù)獲取了 X 型的 next-key lock,那么另外一個事務(wù)在獲取相同范圍的 X 型的 next-key lock 時,是會被阻塞的。
比如,一個事務(wù)持有了范圍為 (1, 10] 的 X 型的 next-key lock,那么另外一個事務(wù)在獲取相同范圍的 X 型的 next-key lock 時,就會被阻塞。
雖然相同范圍的間隙鎖是多個事務(wù)相互兼容的,但對于記錄鎖,我們是要考慮 X 型與 S 型關(guān)系,X 型的記錄鎖與 X 型的記錄鎖是沖突的。
如果和redis無法連接,如何排查原因?
- 網(wǎng)絡(luò)閃斷:先排查網(wǎng)絡(luò)問題,比如檢查網(wǎng)絡(luò)連接是否正常。確保網(wǎng)絡(luò)連接穩(wěn)定,沒有斷開或中斷。檢查網(wǎng)絡(luò)帶寬是否耗盡。如果網(wǎng)絡(luò)帶寬達到極限,可能會導(dǎo)致閃斷。你可以聯(lián)系網(wǎng)絡(luò)管理員或提供商以解決帶寬問題。
- Redis連接拒絕:確認maxclients設(shè)置。在Redis配置文件(redis.conf)中,找到maxclients設(shè)置項,確保其值足夠大以容納你的并發(fā)連接數(shù)。你可以通過修改配置文件來增加maxclients的值。
- 連接溢出:進程可打開的最大文件數(shù)控制(ulimit -n)是限制系統(tǒng)中同時存在的文件描述符數(shù)量的設(shè)置。對于Redis來說,高并發(fā)情況下需要處理大量的連接,因此需要增大該值。
如果redis內(nèi)存不足,你認為是什么原因
- 沒有開啟內(nèi)存淘汰策略,導(dǎo)致 Redis 無法運用內(nèi)存淘汰策略來淘汰不常用的內(nèi)存數(shù)據(jù)。
- 在 Redis 長時間運行的情況下,由于頻繁的插入和刪除操作,可能會導(dǎo)致內(nèi)存碎片,導(dǎo)致可用內(nèi)存減少。
- 單 Redis 節(jié)點的內(nèi)存無法扛住激增的用戶數(shù)據(jù),這時候需要考慮構(gòu)建 Redis 集群來應(yīng)對內(nèi)存不足的問題。
為什么 redis購物車用hash不用 string
使用 Hash 類型來實現(xiàn)購物車有幾個明顯的優(yōu)勢,相比使用 String 類型:
- 結(jié)構(gòu)化數(shù)據(jù):Hash 類型允許將購物車中的每個商品表示為一個字段-值對(field-value pair),其中字段可以是商品的 ID,值可以是商品的數(shù)量或其他屬性。這種結(jié)構(gòu)化可以使得數(shù)據(jù)更加清晰和易于管理
- 節(jié)省內(nèi)存:在 Redis 中,Hash 類型對于存儲小數(shù)量的字段-值對(例如購物車中的商品)通常會使用更高效的內(nèi)存編碼方式。當 Hash 中的成員少于一定數(shù)量時,Redis 會使用壓縮算法來減少內(nèi)存消耗。
- 操作靈活性:使用 Hash 類型,你可以對購物車進行更復(fù)雜的操作,比如只更新某個商品的數(shù)量,而不需要將整個購物車類型的數(shù)據(jù)取出和再存儲。對于多個購物車的操作,Hash 可以顯著提高效率。
- 方便查詢與修改:使用 Hash 類型的購物車,可以更方便地查詢和修改特定商品的信息,例如獲取某個商品的數(shù)量,非常簡單,只需要進行一次 HGET 操作。
講一下Nginx的負載均衡策略
Nginx支持的負載均衡算法包括:
- 輪詢:按照順序依次將請求分配給后端服務(wù)器。這種算法最簡單,但是也無法處理某個節(jié)點變慢或者客戶端操作有連續(xù)性的情況。
- IP哈希:根據(jù)客戶端IP地址的哈希值來確定分配請求的后端服務(wù)器。適用于需要保持同一客戶端的請求始終發(fā)送到同一臺后端服務(wù)器的場景,如會話保持。
- URL哈希:按訪問的URL的哈希結(jié)果來分配請求,使每個URL定向到一臺后端服務(wù)器,可以進一步提高后端緩存服務(wù)器的效率。
- 最短響應(yīng)時間:按照后端服務(wù)器的響應(yīng)時間來分配請求,響應(yīng)時間短的優(yōu)先分配。適用于后端服務(wù)器性能不均的場景,能夠?qū)⒄埱蟀l(fā)送到響應(yīng)時間快的服務(wù)器,實現(xiàn)負載均衡。
- 加權(quán)輪詢:按照權(quán)重分配請求給后端服務(wù)器,權(quán)重越高的服務(wù)器獲得更多的請求。適用于后端服務(wù)器性能不同的場景,可以根據(jù)服務(wù)器權(quán)重分配請求,提高高性能服務(wù)器的利用率。
linux 命令怎么看 cpu 占用率?
可以通過 top 命令來查看系統(tǒng)的 cpu 占用率和各個進程的 cpu 占用率。
圖片
死鎖的時候cpu利用率是高還是低?為什么?
這個關(guān)鍵是看用了什么鎖:
- 如果是用了自旋鎖,拿不到鎖的時候,忙等待,反復(fù)探測鎖狀態(tài),直到拿到鎖,進入臨界區(qū),這種情況會消耗CPU,如果發(fā)生死鎖的話,cpu 利用率就會比較高。
- 如果是互斥鎖,拿不到鎖就讓線程休眠的,這時候就相當于放棄了 cpu,不會消耗 cpu,如果發(fā)生死鎖的話,cpu 利用率就不會升高的。
linux 命令怎么看進程占用的端口?
可以通過 lsof 或者 netstate 命令查看,比如查看 80 端口。
lsof :
[root@xiaolin ~]# lsof -i :80
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 929 root 6u IPv4 15249 0t0 TCP *:http (LISTEN)
nginx 929 root 7u IPv6 15250 0t0 TCP *:http (LISTEN)
nginx 934 nginx 6u IPv4 15249 0t0 TCP *:http (LISTEN)
nginx 934 nginx 7u IPv6 15250 0t0 TCP *:http (LISTEN)
AliYunDun 16507 root 10u IPv4 40212783 0t0 TCP xiaolin:41830->100.100.30.26:http (ESTABLISHED)
netstate:
[root@xiaolin ~]# netstat -napt | grep 80
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 929/nginx: master p