滴滴出行(小桔科技)親身面試經驗分享,java開發崗


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.你還有什么問題?

 


 

 

后記

這些問題的答案本人會繼續完善。

整體感覺,前半部分還可以,后半部分就都不會了,感覺明顯從編程題后難度提升了。

好不容易遇到一個會做的編程題,這次不想又不明不白的涼了。

希望有戲吧……

 


免責聲明!

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



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