面試被懟集(字節跳動篇)


1.面試官:你說說IO和NIO的區別吧:

我:主要的區別就是IO在讀取資源的時候如果讀不到就會阻塞在那里,但是NIO能不能讀到都會立刻返回一個結果,線程可以去做其他事。

面試官:你說tomcat是IO還是NIO模式的

我:tomcat是NIO的啊(這里答錯了,其實IO和NIO tomcat都可以支持,是可以預先配置的,其實大多數都是IO和NIO都可以支持的,包括Netty在內)

面試官:那tomcat去請求一個資源的時候可能需要花費一段時間,幾分鍾的過程,那你說它還是NIO的嗎?

我:。。。。。。。。。。。。。。。。(我覺得哪里不對,像是哪里被混淆了,但是又無法反駁)

 

面試結束思考:我覺得NIO和IO所要解釋的場景不是整個請求過程,而是請求結果,無論是要需要一小時還是兩小時,這個是請求的中間過程,和IO或者NIO模式本身沒有任何關系,IO和NIO是請求之后的完成動作(讀沒讀到資源都算是請求完成),而不是面向請求過程的。

暫時是這樣想的,各位路過大神感興趣的來指點一下謝謝。

 

2.面試官:你是怎么遍歷Map的

我:for(Map.Entry<String,String>entry:a.entrySet()){}

面試官:你還知道其他遍歷方式嗎?

我:也可以用迭代器吧。

面試官:那你為什么用這種方式?

我:因為之前有看到網上說這樣遍歷大量數據的時候比較快

面試官:為什么比較快?

我:。。。。。。。。。

馬后炮:其實面試官這里想問的應該是entrySet和keySet的區別。

keySet():將Map中所有的鍵存入到set集合中。因為set具備迭代器。所有可以迭代方式取出所有的鍵,再根據get方法。獲取每一個鍵對應的值。 keySet():迭代后只能通過get()取key 

  Set<String> keySet = map.keySet();

   Iterator<String> it = keySet.iterator();//有了Set集合,就可以獲取其迭代器。
                
        while(it.hasNext()){
                String key = it.next();
                String value = map.get(key);//有了鍵可以通過map集合的get方法獲取其對應的值。
                        
                System.out.println("key: "+key+"-->value: "+value);//獲得key和value值
                }

entrySet():Set<Map.Entry<K,V>> entrySet() //返回此映射中包含的映射關系的 Set 視圖。 Map.Entry表示映射關系。entrySet():迭代后可以e.getKey(),e.getValue()取key和value。返回的是Entry接口 。

keySet():迭代后只能通過get()取key 
entrySet():迭代后可以e.getKey(),e.getValue()取key和value。返回的是Entry接口 

keySet是鍵的集合,Set里面的類型即key的類型
entrySet是 鍵-值 對的集合,Set里面的類型是Map.Entry

3.面試官:講一下你在線程池里都配置哪些參數:

 

參數名 說明
corePoolSize 線程池維護線程的最少數量
maximumPoolSize 線程池維護線程的最大數量
keepAliveTime 線程池維護線程所允許的空閑時間
workQueue 任務隊列,用來存放我們所定義的任務處理線程
threadFactory 線程創建工廠
handler 線程池對拒絕任務的處理策略

 

面試官:既然你提到拒絕策略,那講一講你都知道哪些拒絕策略吧

1)CallerRunsPolicy:線程調用運行該任務的 execute 本身。此策略提供簡單的反饋控制機制,能夠減緩新任務的提交速度。

  1.  
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
  2.  
    if (!e.isShutdown()) {
  3.  
    r.run();
  4.  
    }
  5.  
    }

這個策略顯然不想放棄執行任務。但是由於池中已經沒有任何資源了,那么就直接使用調用該execute的線程本身來執行。(開始我總不想丟棄任務的執行,但是對某些應用場景來講,很有可能造成當前線程也被阻塞。如果所有線程都是不能執行的,很可能導致程序沒法繼續跑了。需要視業務情景而定吧。)

2)AbortPolicy:處理程序遭到拒絕將拋出運行時 RejectedExecutionException

  1.  
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
  2.  
    throw new RejectedExecutionException();
  3.  
    }

這種策略直接拋出異常,丟棄任務。(jdk默認策略,隊列滿並線程滿時直接拒絕添加新任務,並拋出異常,所以說有時候放棄也是一種勇氣,為了保證后續任務的正常進行,丟棄一些也是可以接收的,記得做好記錄)

3)DiscardPolicy:不能執行的任務將被刪除

publicvoid rejectedExecution(Runnable r, ThreadPoolExecutor e) {}

這種策略和AbortPolicy幾乎一樣,也是丟棄任務,只不過他不拋出異常。

4)DiscardOldestPolicy:如果執行程序尚未關閉,則位於工作隊列頭部的任務將被刪除,然后重試執行程序(如果再次失敗,則重復此過程)

  1.  
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
  2.  
    if (!e.isShutdown()) {
  3.  
    e.getQueue().poll();
  4.  
    e.execute(r);
  5.  
    }
  6.  
    }

 

該策略就稍微復雜一些,在pool沒有關閉的前提下首先丟掉緩存在隊列中的最早的任務,然后重新嘗試運行該任務。這個策略需要適當小心。

4.MyBatis中#與$的區別:

1.兩者都是動態的向sql語句中傳入需要的參數

2.#傳入的參數在SQL中顯示為字符串

     eg:select id,name,age from student where id =#{id},當前端把id值1,傳入到后台的時候,就相當於 select id,name,age from student where id ='1'.

3.$傳入的參數在SqL中直接顯示為傳入的值

    eg:select id,name,age from student where id =${id},當前端把id值1,傳入到后台的時候,就相當於 select id,name,age from student where id = 1.

4.#可以防止SQL注入的風險(語句的拼接)

5.但是如果使用在order by 中就需要使用 $.

我覺得#與的區別最大在於:#{} 傳入值時,sql解析時,參數是帶引號的,而{}傳入值,sql解析時,參數是不帶引號的。

一 : 理解mybatis中 $與#

    在mybatis中的$與#都是在sql中動態的傳入參數。

    eg:select id,name,age from student where name=#{name}  這個name是動態的,可變的。當你傳入什么樣的值,就會根據你傳入的值執行sql語句。

二:使用$與#

   #{}: 解析為一個 JDBC 預編譯語句(prepared statement)的參數標記符,一個 #{ } 被解析為一個參數占位符 。

   ${}: 僅僅為一個純碎的 string 替換,在動態 SQL 解析階段將會進行變量替換。

    傳入一個不改變的字符串或者傳入數據庫字段(列名),例如要傳入order by 后邊的參數

     這種情況下必須使用${}。

綜上,#{}方式一般用於傳入字段值,並將該值作為字符串加到執行sql中,一定程度防止sql注入;

           ${}方式一般用於傳入數據庫對象,例如傳入表名,不能防止sql注入,存在風險。

           模糊查詢中,如果使用如下方式:select * from reason_detail where reason_en like '%${reason}%',此處只能使用$,使用#的話,反而會被解析為列,報錯java.sql.SQLException: Column 'reason' not found

 

 

5.面試官問了一個線程的名字怎么命名還是自定義線程的來着記不太清了:

線程名字的定義:

public final void setName(String name)
public final String getName()

 

當使用Runnable創建線程時,Runnable中並沒有getName和setName,那么想要獲取運行當前代碼的線程名字就需要調用 Thread類中的public static Thread currentThread()獲取當前線程,在調用getName()獲取線程名

publicclassThreadName{publicstaticvoidmain(Stringargs[]){Threadt=newThread(newMyThreadName(),"線程A");t.start();t.run();}}classMyThreadNameimplementsRunnable{@Overridepublicvoidrun(){System.err.println("threa-name="+Thread.currentThread().getName());}}

 

自定義線程:

/*
自定義線程的創建方式:

方式一 : 
    1. 自定義一個類繼承Thread類。
    2. 重寫Thread類的run方法,把自定義線程的任務代碼寫在run方法上。
    3. 創建Thread的子類對象,並且調用start方法啟動一個線程。 
        
    注意:千萬不要直接調用run方法,調用start方法的時候線程就會開啟,線程一旦開啟就會執行run方法中代碼,如果直接調用
    run方法,那么就 相當於調用了一個普通的方法而已。

方式二:
    1. 自定義一個類實現Runnable接口。
    2. 實現Runnable接口 的run方法,把自定義線程的任務定義在run方法上。
    3. 創建Runnable實現類對象。
    4. 創建Thread類 的對象,並且把Runnable實現類的對象作為實參傳遞。
    5. 調用Thread對象 的start方法開啟一個線程。


問題1: 請問Runnable實現類的對象是線程對象嗎?
    Runnable實現類的對象並 不是一個線程對象,只不過是實現了Runnable接口 的對象而已。
    只有是Thread或者是Thread的子類才是線程 對象。

問題2: 為什么要把Runnable實現類的對象作為實參傳遞給Thread對象呢?作用是什么?
    作用就是把Runnable實現類的對象的run方法作為了線程的任務代碼去執行了。

推薦使用: 第二種。 實現Runable接口的。 
原因: 因為java單繼承 ,多實現的。



 */

public class Demo3 implements Runnable{

    @Override
    public void run() {
        /*System.out.println("this:"+ this);
        System.out.println("當前線程:"+ Thread.currentThread());*/
        for(int i = 0 ; i < 100 ; i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
    
    public static void main(String[] args) {
        //創建Runnable實現類的對象
        Demo3 d = new Demo3();
        //創建Thread類的對象, 把Runnable實現類對象作為實參傳遞。
        Thread thread = new Thread(d,"狗娃");  //Thread類使用Target變量記錄了d對象,
        //調用thread對象的start方法開啟線程。
        thread.start();
        
        
        for(int i = 0 ; i < 100 ; i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
        
    } 
    
    /*
      Thread類 的run方法
      
     *  @Override
        public void run() {
            if (target != null) {
                target.run();  //就相當於Runnable實現類的對象的run方法作為了Thread對象的任務代碼了。
            }
        }
    */
}

public classThreadName{publicstaticvoidmain(Stringargs[]){Threadt=newThread(newMyThreadName(),"線程A");t.start();t.run();}}classMyThreadNameimplementsRunnable{@Overridepublicvoidrun(){System.err.println("threa-name="+Thread.currentThread().getName());}}


免責聲明!

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



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