2020騰訊面試題目,附答案


一面:

集合有哪些:

List(ArrayList Linklist ) set(Set Treeset Hashset) map(Hashmap currentHashmap hashtable )

arraylist和linkedlist區別

一個是基於數組的實現 一個是基於的鏈表的實現

hashmap怎么擴容(多線程擴容為什么會死循環),put過程

出現的是鏈表的閉環。

concurrentHashMap 1.7和1.8

1.7是采用采用的還是分段鎖的機制 1.8采用的是CAS機制來實現的。

接口和抽象類區別

JVM內存分區

新生代:

eden,survivor_from, survivor_to

垃圾回收算法:

三種 標記清除 復制算法 標記整理算法

PretenureSizeThreshold,maxTenuringThreshold(默認15)

如果並發的線程數量很多,並且每個線程都是執行一個時間很短的任務就結束了,這樣頻繁創建線程就會大大降低系統的效率,因為頻繁創建線程和銷毀線程需要時間。

1線程池狀態

在ThreadPoolExecutor中定義了一個volatile變量,另外定義了幾個static final變量表示線程池的各個狀態:

volatile int runState;

static final int RUNNING = 0;

static final int SHUTDOWN = 1;

static final int STOP = 2;

static final int TERMINATED = 3;

runState表示當前線程池的狀態,它是一個volatile變量用來保證線程之間的可見性;

下面的幾個static final變量表示runState可能的幾個取值。

當創建線程池后,初始時,線程池處於RUNNING狀態;

如果調用了shutdown()方法,則線程池處於SHUTDOWN狀態,此時線程池不能夠接受新的任務,它會等待所有任務執行完畢;

如果調用了shutdownNow()方法,則線程池處於STOP狀態,此時線程池不能接受新的任務,並且會去嘗試終止正在執行的任務;

當線程池處於SHUTDOWN或STOP狀態,並且所有工作線程已經銷毀,任務緩存隊列已經清空或執行結束后,線程池被設置為TERMINATED狀態。

2任務的執行

ThreadPoolExecutor類中其他的一些比較重要成員變量:

rivate final BlockingQueue<Runnable> workQueue; //任務緩存隊列,用來存放等待執行的任務

private final ReentrantLock mainLock = new ReentrantLock(); //線程池的主要狀態鎖,對線程池狀態(比如線程池大小//、runState等)的改變都要使用這個鎖

private final HashSet<Worker> workers = new HashSet<Worker>(); //用來存放工作集

private volatile long keepAliveTime; //線程存貨時間

private volatile boolean allowCoreThreadTimeOut//是否允許為核心線程設置存活時間

private volatile int corePoolSize; //核心池的大小(即線程池中的線程數目大於這個參數時,提交的任務會被放進任務緩存隊列)

private volatile int maximumPoolSize; //線程池最大能容忍的線程數

private volatile int poolSize; //線程池中當前的線程數

private volatile RejectedExecutionHandler handler; //任務拒絕策略

private volatile ThreadFactory threadFactory; //線程工廠,用來創建線程

private int largestPoolSize; //用來記錄線程池中曾經出現過的最大線程數

private long completedTaskCount; //用來記錄已經執行完畢的任務個數

1)首先,要清楚corePoolSize和maximumPoolSize的含義;

2)其次,要知道Worker是用來起到什么作用的;

3)要知道任務提交給線程池之后的處理策略,這里總結一下主要有4點:

如果當前線程池中的線程數目小於corePoolSize,則每來一個任務,就會創建一個線程去執行這個任務;

如果當前線程池中的線程數目>=corePoolSize,則每來一個任務,會嘗試將其添加到任務緩存隊列當中,若添加成功,則該任務會等待空閑線程將其取出去執行;若添加失敗(一般來說是任務緩存隊列已滿),則會嘗試創建新的線程去執行這個任務;

如果當前線程池中的線程數目達到maximumPoolSize,則會采取任務拒絕策略進行處理;

如果線程池中的線程數量大於 corePoolSize時,如果某線程空閑時間超過keepAliveTime,線程將被終止,直至線程池中的線程數目不大於corePoolSize;如果允許為核心池中的線程設置存活時間,那么核心池中的線程空閑時間超過keepAliveTime,線程也會被終止。

3線程池中的線程初始化

默認情況下,創建線程池之后,線程池中是沒有線程的,需要提交任務之后才會創建線程。

4任務緩存隊列及排隊策略

在前面我們多次提到了任務緩存隊列,即workQueue,它用來存放等待執行的任務

5任務拒絕策略

當線程池的任務緩存隊列已滿並且線程池中的線程數目達到maximumPoolSize,如果還有任務到來就會采取任務拒絕策略,通常有以下四種策略:

ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。

ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。

ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然后重新嘗試執行任務(重復此過程)

ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務

6線程池的關閉

ThreadPoolExecutor提供了兩個方法,用於線程池的關閉,分別是shutdown()和shutdownNow(),其中:

shutdown():不會立即終止線程池,而是要等所有任務緩存隊列中的任務都執行完后才終止,但再也不會接受新的任務

shutdownNow():立即終止線程池,並嘗試打斷正在執行的任務,並且清空任務緩存隊列,返回尚未執行的任務

7線程池容量的動態調整

ThreadPoolExecutor提供了動態調整線程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),

setCorePoolSize:設置核心池大小

setMaximumPoolSize:設置線程池最大能創建的線程數目大小

當上述參數從小變大時,ThreadPoolExecutor進行線程賦值,還可能立即創建新的線程來執行任務。

如果是不采用是這個那就在隊列中的線程是不可能出隊列的,就是如果是的非公平的鎖的話那就永遠不能出隊列。那可能能執行不到該線程。

JVM調優(不太會)

Xms2g:初始化推大小為 2g;

-Xmx2g:堆最大內存為 2g;

-XX:NewRatio=4:設置年輕的和老年代的內存比例為 1:4;

-XX:SurvivorRatio=8:設置新生代 Eden 和 Survivor 比例為 8:2;

–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器組合;

-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器組合;

-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器組合;

-XX:+PrintGC:開啟打印 gc 信息;

-XX:+PrintGCDetails:打印 gc 詳細信息。

如何判斷對象是否應該被回收(引用計數法,可達性分析)

root根包括哪些:對象頭

CMS回收過程,優缺點

並行收集垃圾

初始標記:只是標記一下 GC Roots 能直接關聯的對象,速度很快,仍然需要暫停所有的工作線程。

並發標記:進行 GC Roots 跟蹤的過程,和用戶線程一起工作,不需要暫停工作線程。

重新標記:為了修正在並發標記期間,因用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄,仍然需要暫停所有的工作線程。

並發清除:清除 GC Roots 不可達對象,和用戶線程一起工作,不需要暫停工作線程。由於耗時最長的並發標記和並發清除過程中,垃圾收集線程可以和用戶現在一起並發工作, 所以總體上來看CMS 收集器的內存回收和用戶線程是一起並發地執行:

G1回收過程

類加載過程(加載,驗證,准備,解析,初始化)

雙親委派優點

=

七層模型

物理層 數據鏈路層 網絡層 傳輸層 表示層 應用層 會話層

四次揮手過程(中間狀態也要答)

HttpTCP 在傳輸之前會進行三次溝通,一般稱為“三次握手”,傳完數據斷開的時候要進行四次溝通,一般稱為“四次揮手”。(就是Http的連接和斷開的模式)

第一次握手:主機 A 發送位碼為 syn=1,隨機產生 seq number=1234567 的數據包到服務器,主機 B由 SYN=1 知道, A 要求建立聯機;

第 二 次 握 手 : 主 機 B 收 到 請 求 后 要 確 認 聯 機 信 息 , 向 A 發 送 ack number=( 主 機 A 的seq+1),syn=1,ack=1,隨機產生 seq=7654321 的包

第三次握手: 主機 A 收到后檢查 ack number 是否正確,即第一次發送的 seq number+1,以及位碼ack 是否為 1,若正確, 主機 A 會再發送 ack number=(主機 B 的 seq+1),ack=1,主機 B 收到后確認。

四次揮手:

TCP 建立連接要進行三次握手,而斷開連接要進行四次。這是由於 TCP 的半關閉造成的。因為 TCP 連接是全雙工的(即數據可在兩個方向上同時傳遞)所以進行關閉時每個方向上都要單獨進行關閉。這個單方向的關閉就叫半關閉。當一方完成它的數據發送任務,就發送一個 FIN 來向另一方通告將要終止這個方向的連接。

1關閉客戶端到服務器的連接:首先客戶端 A 發送一個 FIN,用來關閉客戶到服務器的數據傳送,然后等待服務器的確認。其中終止標志位 FIN=1,序列號 seq=u

2服務器收到這個 FIN,它發回一個 ACK,確認號 ack 為收到的序號加 1。

3關閉服務器到客戶端的連接:也是發送一個 FIN 給客戶端。

4客戶段收到 FIN 后,並發回一個 ACK 報文確認,並將確認序號 seq 設置為收到序號加 1。

首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。

為什么TCP能保證不丟失

(滑動窗口,擁塞控制)

HTTP和HTTPS的區別

GET和POST區別

安全性:get 不安全 post 相對安全

傳輸的大小:get的傳輸較小 POST的傳輸較大

數據的來源范式Get是從服務器上獲得數據,而Post則是向服務器傳遞數據的。

mysql全家桶又來了,索引數據結構

采用是B+樹,B+的設計底層數據結構和相關的索引的知識。

為什么用B+樹而不用hash和B-Tree

二叉樹(可能出現全部在左邊和右邊的數據)——>AVL(平衡二叉樹數據大量的時候平衡的時間太多,)——>B Tree(多路平衡查找樹)(數據表中的數據都是存儲在頁中的,所以一個頁中能存儲多少行數據呢指針少的情況下要保存大量數據,只能增加樹的高度,導致IO操作變多,查詢性能變低)——>B+ Tree的一個演變的過程來進行分析,為什么使用B+ Tree的?B+ Tree,都放在了葉子節點上。提高了檢索的效率。預讀原理,因為B+ Tree無 data 域,其實就是因為沒有date域了,但是每次IO的頁的大小是固定的,每次IO讀取若干個塊塊中包含的Key域的值肯定更多啊,B+樹單次磁盤IO的信息量大於B樹,從這點來看B+樹相對B樹磁盤 IO 次數少。利用了磁盤預讀原理,將一個節點的大小設為等於一個頁,這樣每個節點只需要一次I/O就可以完全載入。1、B+Tree中因為數據都在葉子節點,所以每次查詢的時間復雜度是固定的,因為穩定性保證了2、而且葉子節點之間都是鏈表的結構,所以B+Tree也是可以支持范圍查詢的,而B樹每個節點 key 和 data 在一起,則無法區間查找。

InooDB和MyISAM的區別(事務,聚集索引,鎖的粒度等)

回表,聯合索引查詢會不會用到索引系列問題

下面我們來假設一種情況,一個表有三個字段 ID ,name ,age,我將ID設置成主鍵索引,name設成輔助索引。然后來看一下下面的sql:

1.select * from t where id='5';

2.select * from t where name='張三';

兩個很簡單的Sql,第一個sql不用說,直接通過主鍵索引,從樹上直接可以得到結果,那第二個sql:首先name,mysql並不能得到所有列的信息(也就是 ),他只能得到主鍵ID,然后他會根據ID在進行二次查詢,這就引發了--回表問題。這就是為啥不能使用 的原因。那么怎么解決那:第一不要寫*,第二利用組合索引,也就是說你根據業務實際需要,將需要的字段形成組合索引。

所以是會用到的索引的。

最左匹配是什么意思,聯合索引建立索引過程

在Mysql建立多列索引(聯合索引)有最左前綴的原則,即最左優先。假設我們有兩列a,b,a和b是聯合索引,他的順序是a,b,我們在where語句中調用a=? and b=?的時候就會走聯合索引,如果調用where a = ?的時候也會走索引,但是當我們使用where b = ?的時候就不會走這個聯合索引。

成因:mysql創建復合索引的規則是首先會對復合索引的最左邊,也就是索引中的第一個字段進行排序,在第一個字段排序的基礎上,在對索引上第二個字段進行排序,其實就像是實現類似order by 字段1,字段2這樣的排序規則,那么第一個字段是絕對有序的,而第二個字段就是無序的了,因此一般情況下直接只用第二個字段判斷是用不到索引的,這就是為什么mysql要強調聯合索引最左匹配原則的原因。

獨占所,共享鎖,樂觀鎖講一下

寫鎖是獨占鎖 ,在這個期間是不允許的任何線程來操作對象的。 讀鎖就是共享鎖,可以使讓其他線程的來讀取的,但是不允許有修改。樂觀鎖是共享鎖的一種,在通過樂觀鎖的時候獲取對象的時候先比較一下樂觀鎖的版本號。如果版本號是正確的,那就可以獲取對象。如果是版本不對的話。那就是不允許修改的。

mysql分庫分表?

垂直拆分

垂直分庫是根據數據庫里面的數據表的相關性進行拆分,比如:一個數據庫里面既存在用戶數據,又存在訂單數據,那么垂直拆分可以把用戶數據放到用戶庫、把訂單數據放到訂單庫。垂直分表是對數據表進行垂直拆分的一種方式,常見的是把一個多字段的大表按常用字段和非常用字段進行拆分,每個表里面的數據記錄數一般情況下是相同的,只是字段不一樣,使用主鍵關聯

垂直拆分的優點是:

可以使得行數據變小,一個數據塊(Block)就能存放更多的數據,在查詢時就會減少I/O次數(每次查詢時讀取的Block 就少)

可以達到最大化利用Cache的目的,具體在垂直拆分的時候可以將不常變的字段放一起,將經常改變的放一起

數據維護簡單

垂直拆分缺點是:

主鍵出現冗余,需要管理冗余列

會引起表連接JOIN操作(增加CPU開銷)可以通過在業務服務器上進行join減少數據庫壓力

依然存在單表數據量過大的問題(需要水平拆分)

事務處理復雜

水平拆分

水平拆分是通過某種策略將數據分片來存儲,分庫內分表和分庫兩部分,每片數據會分散到不同的MySQL表或庫,達到分布式的效果,能夠支持非常大的數據量。前面的表分區本質上也是一種特殊的庫內分表 庫內分表,僅僅是單純的解決了單一表數據過大的問題,由於沒有把表的數據分布到不同的機器上,因此對於減輕MySQL服務器的壓力來說,並沒有太大的作用,大家還是競爭同一個物理機上的IO、CPU、網絡,這個就要通過分庫來解決

水平拆分的優點是:

不存在單庫大數據和高並發的性能瓶頸

應用端改造較少

提高了系統的穩定性和負載能力

缺點是:

分片事務一致性難以解決

跨節點Join性能差,邏輯復雜

數據多次擴展難度跟維護量極大

分片原則

能不分就不分,參考單表優化

分片數量盡量少,分片盡量均勻分布在多個數據結點上,因為一個查詢SQL跨分片越多,則總體性能越差,雖然要好於所有數據在一個分片的結果,在必要的時候進行擴容,增加分片數量

分片規則需要慎重選擇做好提前規划,分片規則的選擇,需要考慮數據的增長模式,數據的訪問模式,分片關聯性問題,以及分片擴容問題,最近的分片策略為范圍分片,枚舉分片,一致性Hash分片,這幾種分片都有利於擴容

盡量不要在一個事務中的SQL跨越多個分片,分布式事務一直是個不好處理的問題

查詢條件盡量優化,盡量避免Select * 的方式,大量數據結果集下,會消耗大量帶寬和CPU資源,查詢盡量避免返回大量結果集,並且盡量為頻繁使用的查詢語句建立索引。

通過數據冗余和表分區賴降低跨庫Join的可能。

這里特別強調一下分片規則的選擇問題,如果某個表的數據有明顯的時間特征,比如訂單、交易記錄等,則他們通常比較合適用時間范圍分片,因為具有時效性的數據,我們往往關注其近期的數據,查詢條件中往往帶有時間字段進行過濾,比較好的方案是,當前活躍的數據,采用跨度比較短的時間段進行分片,而歷史性的數據,則采用比較長的跨度存儲。

總體上來說,分片的選擇是取決於最頻繁的查詢SQL的條件,因為不帶任何Where語句的查詢SQL,會遍歷所有的分片,性能相對最差,因此這種SQL越多,對系統的影響越大,所以我們要盡量避免這種SQL的產生。

sql優化

1字段的優化

2查詢的優化

3索引的優化

4讀寫分離

5分庫分表的操作

6數據庫集群的操作

線程和進程概念(共享哪些區域)

1.堆

幾乎所有對象實例被分配到這里,也是垃圾收集器管理的主要區域。Java堆可以被分為新生代和老生代。進一步划分,則有Eden空間、From Survivor空間、To Survivor空間等。無論如何划分,都是為了更好地回收內存、更快的分配內存。

  1. 方法區

方法區由於存儲虛擬機加載的類的信息、常量、靜態變量、JIT編譯后的代碼等。

虛擬內存講一下(分頁)

synchronized和Lock的區別

一個是通過指令集來實現鎖住的對象的頭來實現加鎖的。

一個是通過設置一個標志位置來鎖住獨享的。

volatile的作用

JMM內存模型和緩存一致性協議還有就是的一個是保持可見性的

算法題:存儲有[0,n)的數組,數組長度為len。只能交換數組里n和0的位置進行排序

/*查詢學生表中姓名、學號,並以學號降序排序*/
select name,StuID from Students_information order by StuID desc /**order by 以什么排序,默認為升序,desc是降序*/ /*查詢學生表中前5名學生的姓名,學號,並以學號升序排列*/ select top 5 name,StuID from Students_information order by StuID /*order by 默認為升序*/

二面:

項目問題10分鍾,問到了Hash沖突

利用是數組+鏈表來解決hash沖突

synchronized底層實現(markWord,entrySet,waitSet)

通過鎖住對象的頭部來實現對對象加鎖,synchronize的關鍵字在以前是使用的指令來實現的

他屬於獨占式的悲觀鎖,同時屬於可重入鎖。代碼塊同步是使用monitorenter和monitorexit指令實現的。monitorenter指令是在編譯后插入到同步代碼塊的開始位置,而monitorexit是插入到方法結束處和異常處,JVM要保證每個monitorenter必須有對應的monitorexit與之配對任何對象都有一個monitor與之關聯,當且一個monitor被持有后,它將處於鎖定狀態。

在Java中,鎖共有4種狀態,級別從低到高依次為:無狀態鎖,偏向鎖,輕量級鎖和重量級鎖狀態,這幾個狀態會隨着競爭情況逐漸升級。鎖可以升級但不能降級。?通過一個標志位來判斷 兩個位置00表示的4種鎖

AQS底層實現(非公平鎖,公平鎖)

底層采用的是雙鏈表的來實現了的,公共鎖的定義是所有的對象在獲取鎖的時候都是需要進入隊列的。非公平鎖是在對象獲取鎖的時候是采用是首先查看鎖是否為空,如果是空的話,那就可以對獲取,如果是有對象持有的話,那就進入隊列進行排隊。

Spring ICO,AOP介紹

SpringIOC是spring對提供了對類的全生命周期的管理的一種思想,利用反射機制來實現的對Bean的實例化產生和創建和銷毀這樣的機制。來實現對類對的屬性的控制,這個過程中Bean實例和生命周期是SpringIOC中最重要的。Spring的Bean產生詳細請見其他。

SpringAOP是一種切向編程的思想。在傳統的過程時候由於是在傳統的架構中都是垂直的流程體系。但是在這個過程中經常產生一些橫向問題,比如log日志記錄,權限驗證,事務處理,性能檢查的問題,為了遵循軟件的開閉原則。就是對原來不修改進而擴展原累的方法和功能。SpringAOP就是實現了這樣一種思想。通過對原方法和類在不修改代碼的情況下而進行了類的增強的方式。

Spring用到了什么設計模式

工廠模式:BeanFactory就是簡單工廠模式的體現,用來創建對象的實例;

單例模式:Bean默認為單例模式。

代理模式:Spring的AOP功能用到了JDK的動態代理和CGLIB字節碼生成技術;

模板方法:用來解決代碼重復的問題。比如. RestTemplate, JmsTemplate, JpaTemplate。

觀察者模式:定義對象鍵一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴於它的對象都會得到通知被制動更新,如Spring中listener的實現–ApplicationListener。

單例為什么加鎖,volatile什么作用

單例模式中有一種是懶漢模式。這樣的模式是會產生的線程安全的問題。volatile是讓變量在多線程的情況下保持對其他線程的可見。

hashmap什么時候用到了紅黑樹

當鏈表的節點超過8個時候采用紅黑樹來實現存儲。

介紹紅黑樹特點,為什么不用AVL樹

紅黑樹屬於平衡二叉樹。它不嚴格是因為它不是嚴格控制左、右子樹高度或節點數之差小於等於1,但紅黑樹高度依然是平均log(n),且最壞情況高度不會超過2log(n)。紅黑樹的插入效率比AVL的數要高。

紅黑樹不追求"完全平衡",即不像AVL那樣要求節點的 |balFact| <= 1,它只要求部分達到平衡,但是提出了為節點增加顏色,紅黑是用非嚴格的平衡來換取增刪節點時候旋轉次數的降低,任何不平衡都會在三次旋轉之內解決,而AVL是嚴格平衡樹,因此在增加或者刪除節點的時候,根據不同情況,旋轉的次數比紅黑樹要多

算法題:一個鏈表:奇數序號升序,偶數序號降序,要求做這個鏈表的整體升序排序

private static ListNode[] splitList(ListNode head) { ListNode cur = head; ListNode head1 = null; ListNode head2 = null; ListNode cur1 = null; ListNode cur2 = null; int num = 1; while (head != null) { if (num % 2 == 1) { if (cur1 != null) { cur1.next = head; cur1 = cur1.next; } else { cur1 = head; head1 = cur1; } } else { if (cur2 != null) { cur2.next = head; cur2 = cur2.next; } else { cur2 = head; head2 = cur2; } } head = head.next; num++; } cur1.next = null; cur2.next = null; ListNode[] heads = new ListNode[]{head1, head2}; return heads; } private static ListNode reverseList(ListNode head) { ListNode pre = null; ListNode cur = head; while (cur != null) { ListNode next = cur.next; cur.next = pre; pre = cur; cur = next; } return pre; } private static ListNode mergeLists(ListNode head1, ListNode head2) { if (head1 == null && head2 == null) { return null; } if (head1 == null || head2 == null) { return head1 == null ? head2 : head1; } ListNode first = new ListNode(-1); ListNode cur = first; while (head1 != null && head2 != null) { if (head1.val < head2.val) { cur.next = head1; head1 = head1.next; } else { cur.next = head2; head2 = head2.next; } } cur.next = head1 == null ? head2 : head1; return first.next; }

三面:

介紹了兩個項目

怎么解決超賣(答:redis + mysql樂觀鎖)

職業規划 + 想成為tech lead應該應該具備什么條件

現在有哪些offer


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM