2020.11.25
概要
本次面試是最近剛面的。
PS:本人java開發2年經驗,這次面的是滴滴出行(小桔科技)java開發崗。
2020.11.30
滴滴又打來電話了, 預定12.3.星期四面試,不過是另一個java崗位,還是一面;
說明這次的涼了,然后又被HR撈起來了……
很迷,我都不知道該說什么好……
等面完后再總結一篇新的文章吧……
過程
1.2020年11月16日,本人投給滴滴的簡歷變成了"被查看"狀態(也是在拉勾APP投的),不過多會投的就忘了,應該也是前一二天吧。
PS:投給字節跳動的簡歷還是"投遞成功"狀態,甚至沒有被查看,看來字節跳動是真的不在拉勾上更新簡歷進度,不過個人覺得這個也不是很重要,只是記錄一下。
2.2020年11月23日,本人接到了滴滴的電話,預約面試時間,本人預約了11月24日20:30的面試。
3.然后收到了郵件,其中寫着面試時間,以及面試方式;這次要使用騰訊會議PC版視頻面試。
4.2020年11月24日,18:00,HR又打來電話,詢問面試時間是否有調整,很周到;本人回復不用調整。
5.2020年11月24日,20:30-21:15,進行了滴滴視頻面試。
6.今天25日,等待結果中,希望無論如何給一個結果通知……
面試內容
1.自我介紹。
*期間面試官自言自語說本人工作時間不長,本人目前2年java開發經驗,如果還不夠的話,難道是必須要3-5年?
2.詢問做過的項目,主要問項目問的比較多,以及項目細節。
3.你的項目業務比較復雜嗎?
答:是的。然后介紹了一個大批量推送的需求是如何實現的。
4.接第三問:你剛才介紹的是技術實現復雜,不是業務復雜。
答:又擴展介紹了一下項目流程,不過面試官認為又回歸到介紹技術實現復雜了;只好回答,那它可能並沒有那么復雜。
5.編寫一個程序,有三個線程,分別輸出A/B/C,現在讓它們按順序輸出ABC,並循環十次,你能想到幾種實現思路?
答:使用公平鎖Reentrantlock(true)實現;有一個java線程池也可以實現;使用線程的wait()與notify()也可以實現。(synchronized個人感覺不能實現,這個是非公平鎖,不講順序)
6.編碼實現第五題(點擊騰訊會議的共享屏幕),參考答案如下(終於碰到一個可以做出來的編程題了):
首先創建一個自定義線程類,准備使用Reentrantlock:
import java.util.concurrent.locks.ReentrantLock; public class MyThread extends Thread{ private ReentrantLock lock; private String str; public MyThread(String str, ReentrantLock lock){ this.str = str; this.lock = lock; } @Override public void run() { for(int i = 0; i<10; i++){ lock.lock(); System.out.println(str); lock.unlock(); } } }
然后是main方法:
import java.util.concurrent.locks.ReentrantLock; public class Test { public static void main(String[] args) { //有三個線程,分別輸出ABC,現在要求線程按順序輸出並且循環10次 ReentrantLock lock = new ReentrantLock(true); MyThread t1 = new MyThread("A",lock); MyThread t2 = new MyThread("B",lock); MyThread t3 = new MyThread("C",lock); t1.start(); t2.start(); t3.start(); } }
這樣就實現了題目要求。
*之后明顯感覺難度開始上升。
7.你知道volatile關鍵字解析嗎?(從來沒聽過)
百度:
https://blog.51cto.com/12222886/1964228 https://www.cnblogs.com/dolphin0520/p/3920373.html ●Java中的volatile 在Java程序中,如果一個變量被volatile關鍵字修飾,那么這個變量就具有了有序性和可見性。 有序性:java語言中提供了synchronized和volatile兩個關鍵字保證線程之間操作的有序性,也就是他可以使CPU指令有序。 可見性:當一個線程操作一個被volatile修飾的變量時,這個變量的修改對其他所有線程都是可見的,因為此時的操作不會將該變量讀到當前線程的CPU緩存中進行操作,而是直接操作內存 ●個人理解與總結 volatile修飾變量后,這個變量會存入內存,變成共享變量,線程讀寫時直接操作內存中的這個變量,跳過CPU cache這一步,因此在讀取這個變量時總會返回最新寫入的值; 並且,在操作這個變量時是有序的,CPU指令有序; 並且,在訪問volatile變量時不會執行加鎖操作,也就不會使執行線程阻塞,因此volatile變量是一種比synchronized關鍵字更輕量級的同步機制。 ●synchronized原理 synchronized可以修飾方法、對象、類;在修飾方法時又分為實例方法、靜態方法、代碼塊。 對於同步塊的實現使用了monitorenter和monitorexit指令:他們隱式的執行了Lock和UnLock操作,用於提供原子性保證。 monitorenter指令插入到同步代碼塊開始的位置、monitorexit指令插入到同步代碼塊結束位置,jvm需要保證每個monitorenter都有一個monitorexit對應。 這兩個指令,本質上都是對一個對象的監視器(monitor)進行獲取,這個過程是排他的,也就是說同一時刻只能有一個線程獲取到由synchronized所保護對象的監視器。 線程執行到monitorenter指令時,會嘗試獲取對象所對應的monitor所有權,也就是嘗試獲取對象的鎖;而執行monitorexit,就是釋放monitor的所有權。 詳情見:https://www.cnblogs.com/wuzhenzhao/p/10250801.html
8.數據庫有兩個條件分別加了索引,按照兩個條件查詢時索引怎么走(兩個索引同時用嗎?不知道)
百度與個人總結(MySql):
(1)首先,索引可以給一個字段加,也可以給多個字段加,如圖(Navicat):
其中,名是自己起的,欄位對應表中的字段;
(2)加索引之后,當select對應字段、或group by、或order by、或其它增刪改查時,都可能會用到索引,提高sql執行效率。
如果經常用到多個字段(例如group by或order by多個字段),就應該給多個字段加一條索引。
關於order by,只有出現在where條件中,才可能會走索引;group by等也類似。詳情見:https://www.cnblogs.com/zhaoyl/archive/2012/05/04/2483513.html
(3)如果兩個條件都加了索引,sql執行時,會選擇影響行數較少的索引,即區分度大的索引。(使用explain分析結果時,rows的值小的。)詳情見:https://blog.csdn.net/qq_22771739/article/details/85853620
9.如何知道一句sql走了哪個索引?(好像是有一個語法是調試sql用的,然而忘了,還是不常用)
百度:參考網址:https://www.cnblogs.com/wqbin/p/12124621.html
使用explain,可以查看sql是否走了索引
explain select * from test group by user
之后,可以查看結果(本人用Navicat執行的sql)。
舉個例子:
(1)數據庫表
(2)表設計
(3)索引(自己設置的)
(4)執行【explain select * from test group by user】,返回結果:
其中,type為index,說明有索引;(type結果值從好到壞依次是:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL)
key為實際使用的索引,user_index,說明使用了這個字段的索引。
possible_keys為可能需要使用的索引,正常情況下與key相同;這里為空,說明可能用不到索引也能高效完成查詢(不過后來發現還是用索引好)。詳細原因可見:https://blog.csdn.net/eden_Liang/article/details/108026148
(5)執行【explain select * from test 】返回結果:
可以看到,type為ALL,並且key為空,說明沒有使用索引。
10.線程池實現原理(只基本會用,沒研究過原理)
百度:
為什么需要使用線程池?
●當使用大量線程時,減少大量的new線程與GC回收線程的額外開銷。
首先,java有四種線程池:
●newSingleThreadExecutor
創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
●newFixedThreadPool
創建一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待。
●newScheduledThreadPool
創建一個可定期或者延時執行任務的定長線程池,支持定時及周期性任務執行。
●newCachedThreadPoo
創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
原理:
所謂線程池本質是一個hashSet。多余的任務會放在阻塞隊列中。
只有當阻塞隊列滿了后,才會觸發非核心線程的創建。所以非核心線程只是臨時過來打雜的。直到空閑了,然后自己關閉了。
線程池提供了兩個鈎子(beforeExecute,afterExecute)給我們,我們繼承線程池,在執行任務前后做一些事情。
線程池原理關鍵技術:鎖(lock,cas)、阻塞隊列、hashSet(資源池)
詳情網址:
https://www.cnblogs.com/rinack/p/9888717.html
https://www.cnblogs.com/franson-2016/p/13291591.html
11.線程interrupt()作用(已經被問蒙了,這個也不會了)
百度:
interrupt() 方法只是改變中斷狀態而已,它不會中斷一個正在運行的線程。 *相當於只是改變了isIntrerrupt()返回的boolean值。(當然還有些其它操作,不過主要是改變這個值,其它方法中會根據這個方法判斷當前線程狀態。) 如果線程阻塞前調用這個方法,那么當該線程遇到阻塞時,會拋異常,停止運行; 如果線程處於阻塞狀態,調用這個方法,也能讓線程拋異常,停止運行。 更確切的說,如果線程被Object.wait, Thread.join和Thread.sleep三種方法之一阻塞,此時調用該線程的interrupt()方法,那么該線程將拋出一個 InterruptedException中斷異常(該線程必須事先預備好處理此異常),從而提早地終結被阻塞狀態。 如果線程沒有被阻塞,這時調用 interrupt()將不起作用,直到執行到wait(),sleep(),join()時,才馬上會拋出 InterruptedException。 詳情見:https://blog.csdn.net/liujian8654562/article/details/79875853
再總結下線程基本方法:
●線程有五種狀態,新建,就緒,運行,阻塞,死亡 ●new Thread()創建的線程,是新建狀態的。 ●使用start()方法,線程進入就緒狀態;等待獲得資源后,執行run()方法中的內容,此時算運行狀態。 ●線程運行狀態中,遇到某些情況時會進入阻塞狀態,如需要執行輸入輸出但是資源不足;或者人為使用sleep(),suspend(),wait()方法,也會讓線程處於阻塞狀態。 ●線程調用stop(),destory(),或者run()方法執行完畢,就會進入死亡狀態。 ●sleep()方法可以讓線程等待一段時間后再運行,此時是阻塞狀態,不釋放資源;此時會讓出cpu,但是不釋放鎖。 ●wait()方法可以讓線程進入阻塞狀態,釋放資源;JVM把這個線程放入等待池;需要等待其它線程使用notify()或notifyAll() ●wait(long timeout)方法可以讓線程進入阻塞狀態,釋放資源;需要等待其它線程使用notify()或notifyAll(),或者超過時間后,這個線程變為就緒狀態 ●notify()方法可以讓線程從阻塞狀態變為就緒狀態,JVM把這個線程從等待池中取出;一般是其它線程調用這個方法。 ●suspend()使線程阻塞,resume()喚醒線程,這兩個方法及其它所有方法在線程阻塞時都不會釋放占用的鎖(如果占用了的話);而wait()和notify()這一對方法會釋放鎖。 ●yield()的作用是讓步,它能夠讓當前線程從“運行狀態”進入到“就緒狀態”,從而讓其他等待線程獲取執行權,但是不能保證在當前線程調用yield()之后,其他線程就一定能獲得執行權,也有可能是當前線程又回到“運行狀態”繼續運行。 詳情見:https://blog.csdn.net/wordwarwordwar/article/details/85924858 ●需要注意,sleep(),suspend(),resume(),yield(),start(),stop(),destory()這些方法的主體是線程,如new Thread().suspend(); ●而wait(),notify(),notifyAll()的主體可以是任何對象,例如new String().wait();具體使用方式見下方網址: 關於notify()與notifyAll():https://blog.csdn.net/qq_42547338/article/details/107448668 ●還需要注意,java中,destory()方法並沒有被實現,例如new Thread().destory(),調用該方法只會拋出一個異常:NoSuchMethodError();這個方法需要自己繼承Thread類后重寫。 ●還需要注意,直接使用stop(),會立即停止線程,可能導致文件流、數據庫連接沒有關閉,因此不推薦使用。推薦標志位與interrupt()等組合使用來停止線程,詳細使用如下(記住只用interrupt()是不會真的停止線程的): https://www.cnblogs.com/liyutian/p/10196044.html
12.你還有什么問題?
后記
這些問題的答案本人會繼續完善。
整體感覺,前半部分還可以,后半部分就都不會了,感覺明顯從編程題后難度提升了。
好不容易遇到一個會做的編程題,這次不想又不明不白的涼了。
希望有戲吧……