2019面試

和信貸面試

HashMap的hash是如何計算的? 負載因子是什么?什么時候rehash的?

  • hash的代碼如下。
  static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

Java 左移運算 << ,丟棄最高位,0補最低位;帶符號右移運算 >> 符號位不變,左邊補上符號位;無符號右移運算 >>> 忽略了符號位擴展,0補最高位

^ 是異或運算符。如果a、b兩個值不相同,則異或結果為1。如果a、b兩個值相同,異或結果為0

  • 負載因子 loadFactor,默認值是0.75。 threshold = loadFactor * capacity。

  • 什么時候rehash呢?

    • 插入數(shù)據(jù)時,如果 transient Node<K,V>[] table 變量為null,則進行初始化。

    • putVal成功之后,size代表當前HashMap中的元素個數(shù)。

      if (++size > threshold)
          resize();
      

ConcurrentHashMap是如何實現(xiàn)多線程安全的呢?

  • jdk1.8中,使用cas + sychornized 來實現(xiàn)線程安全。

ReentrantLock 的實現(xiàn)原理

  • ReentrantLock 實現(xiàn)鎖的底層原語是LockSupport.park()?unpark()?
  • 在調用lock()方法是,會首先用cas樂觀鎖嘗試獲取鎖,在獲取鎖失敗的情況下會將當前線程封裝成一個node存入一個雙端隊列。
  • 在調用unlock()方法時,如果隊列不為空,則會將隊列中的下一個線程喚醒。
  • 如果不使用雙端隊列,還有別的實現(xiàn)方式嗎?這里回答的不太好,說官方的實現(xiàn)方式即是最優(yōu)的實現(xiàn)方式,暫時想不到最優(yōu)的實現(xiàn)方式。

分布式鎖的實現(xiàn)方式

  • redis setnx(key,value,expiretime)
    • 實現(xiàn)原理
  • zookeeper
    • zookeeper在創(chuàng)建分布式鎖時會在leader節(jié)點創(chuàng)建一個目錄,當有 (n / 2) + 1 個節(jié)點返回ack時,鎖即獲取成功。

redis 實現(xiàn)原理 (技術總監(jiān)面)

  • redis 網(wǎng)絡模型, 純內存操作,高效的數(shù)據(jù)結構,數(shù)據(jù)備份方式四個主題一一詳細的詢問。

Mysql (技術總監(jiān)面)

  • MySQL 索引類型(B+Tree, Hash), B+tree是一個什么樣的數(shù)據(jù)結構。
  • 分布分表
    • 分庫分表策略
    • 當分庫分表達到數(shù)千個數(shù)據(jù)庫時,如何進行匯總查詢(要求在一個http request response timeout 內返回)。
      • 我回答將查詢同時發(fā)送到數(shù)千個實例,取到結果后本地內存merge , sort 。但是對此回答不滿意,后來沒有思路了,只能說在MySQL技術范疇之內我的技術儲備能夠解決的方法僅此一個了。

火花思維面試

手寫代理模式(技術總監(jiān)面)

dubbo 一次request發(fā)送完畢之后,如何獲取相應結果的 (技術總監(jiān)面,他說dubbo請求一個請求拿到結果之后下一個才能發(fā)送請求)

  • 在 dubbo org.apache.dubbo.rpc.protocol.dubbo.ChannelWrappedInvoker#doInvoke 中可以看到currentClient.request(inv).get()這行請求的關鍵代碼

     if (closed) {
                throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
            }
            // create request.
            Request req = new Request();
            req.setVersion(Version.getProtocolVersion());
            req.setTwoWay(true);
            req.setData(request);
            DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout);
            try {
                channel.send(req);
            } catch (RemotingException e) {
                future.cancel();
                throw e;
            }
            return future;
    

這行代碼的調用如上,可以看到dubbo client 組裝了請求參數(shù)之后,就通過 `

  •  if (closed) {
                throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
            }
            // create request.
            Request req = new Request();
            req.setVersion(Version.getProtocolVersion());
            req.setTwoWay(true);
            req.setData(request);
            DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout);
            try {
                channel.send(req);
            } catch (RemotingException e) {
                future.cancel();
                throw e;
            }
            return future;
    
這行代碼的調用如上,可以看到dubbo client 組裝了請求參數(shù)之后,就通過`DefaultFuture.newFuture(channel, req, timeout)` 構造了一個DefaultFuture 。通過 channel將請求`send`即返回。這里并沒有拿到response 。但是在返回`DefaultFuture`之后,在`dubbo org.apache.dubbo.rpc.protocol.dubbo.ChannelWrappedInvoker#doInvoke `中的 `currentClient.request(inv)`拿到`DefaultFuture`之后即調用`get`方法返回了RPC請求的結果。

```java
if (timeout <= 0) {
            timeout = Constants.DEFAULT_TIMEOUT;
        }
        if (!isDone()) {
            long start = System.currentTimeMillis();
            lock.lock();
            try {
                while (!isDone()) {
                    done.await(timeout, TimeUnit.MILLISECONDS);
                    if (isDone() || System.currentTimeMillis() - start > timeout) {
                        break;
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
            if (!isDone()) {
                throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
            }
        }
        return returnFromResponse();
```

get的實際邏輯如上,線程在請求參數(shù)發(fā)送出去之后即進入while循環(huán)中進行阻塞等待,通過設置阻塞等待的時間,如果超過指定時間服務端還沒有相應結果則會跳出循環(huán),拋出超時異常。如果在timeout時間范圍內RPC響應結果返回,則 有如下代碼

```java
private void doReceived(Response res) {
        lock.lock();
        try {
            response = res;
            done.signalAll();
        } finally {
            lock.unlock();
        }
        if (callback != null) {
            invokeCallback(callback);
        }
    }
```

此時將會喚醒前面`get` 方法 while中的線程阻塞, 將結果返回。這就是一個請求響應的過程。

## redis 集群的幾種實現(xiàn)方式(技術總監(jiān)面)

> 這個問題回答的不是很好,只是簡單的說是通過 redisClient 來實現(xiàn)的客戶端分片,redis集群互相不感知。然后被問到當集群中有一臺掛了會有什么樣的結果時,就說到了一致性hash。當問掛了的數(shù)據(jù)對線上的操作造成影響時怎么辦,然后回答說通過redis一主一叢。但是這個問題大體上來回回答的不是特別好。應當明確redis實現(xiàn)集群的幾種方式(客戶端分片,redis 原生cluster等),實現(xiàn)原理,`故障應對策略`等。

##  

# 惠金所面試(技術一面)

兩個排序的數(shù)組merge到一個大的數(shù)組里。手寫代碼

業(yè)務場景測試 (線上活動7天,給一個文件,里面有n(百萬級別)的活動碼,用戶若想獲取獎品,就必須提將指定的活動碼傳遞到服務器,一個用戶只能獲取一個獎品)

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容