和信貸面試
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兩個值不相同评汰,則異或結(jié)果為1。如果a痢虹、b兩個值相同被去,異或結(jié)果為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()?
- 在調(diào)用lock()方法是,會首先用cas樂觀鎖嘗試獲取鎖健无,在獲取鎖失敗的情況下會將當前線程封裝成一個node存入一個雙端隊列荣恐。
- 在調(diào)用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)原理 (技術(shù)總監(jiān)面)
- redis 網(wǎng)絡(luò)模型检访, 純內(nèi)存操作,高效的數(shù)據(jù)結(jié)構(gòu)仔掸,數(shù)據(jù)備份方式四個主題一一詳細的詢問脆贵。
Mysql (技術(shù)總監(jiān)面)
- MySQL 索引類型(B+Tree, Hash), B+tree是一個什么樣的數(shù)據(jù)結(jié)構(gòu)。
- 分布分表
- 分庫分表策略
- 當分庫分表達到數(shù)千個數(shù)據(jù)庫時起暮,如何進行匯總查詢(要求在一個http request response timeout 內(nèi)返回)卖氨。
- 我回答將查詢同時發(fā)送到數(shù)千個實例,取到結(jié)果后本地內(nèi)存merge , sort 负懦。但是對此回答不滿意筒捺,后來沒有思路了,只能說在MySQL技術(shù)范疇之內(nèi)我的技術(shù)儲備能夠解決的方法僅此一個了密似。
火花思維面試
手寫代理模式(技術(shù)總監(jiān)面)
dubbo 一次request發(fā)送完畢之后,如何獲取相應(yīng)結(jié)果的 (技術(shù)總監(jiān)面焙矛,他說dubbo請求一個請求拿到結(jié)果之后下一個才能發(fā)送請求)
-
在 dubbo org.apache.dubbo.rpc.protocol.dubbo.ChannelWrappedInvoker#doInvoke 中可以看到
currentClient.request(inv).get()
這行請求的關(guān)鍵代碼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;
這行代碼的調(diào)用如上,可以看到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;
這行代碼的調(diào)用如上村斟,可以看到dubbo client 組裝了請求參數(shù)之后,就通過`DefaultFuture.newFuture(channel, req, timeout)` 構(gòu)造了一個DefaultFuture 抛猫。通過 channel將請求`send`即返回蟆盹。這里并沒有拿到response 。但是在返回`DefaultFuture`之后闺金,在`dubbo org.apache.dubbo.rpc.protocol.dubbo.ChannelWrappedInvoker#doInvoke `中的 `currentClient.request(inv)`拿到`DefaultFuture`之后即調(diào)用`get`方法返回了RPC請求的結(jié)果逾滥。
```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)中進行阻塞等待败匹,通過設(shè)置阻塞等待的時間寨昙,如果超過指定時間服務(wù)端還沒有相應(yīng)結(jié)果則會跳出循環(huán),拋出超時異常掀亩。如果在timeout時間范圍內(nèi)RPC響應(yīng)結(jié)果返回舔哪,則 有如下代碼
```java
private void doReceived(Response res) {
lock.lock();
try {
response = res;
done.signalAll();
} finally {
lock.unlock();
}
if (callback != null) {
invokeCallback(callback);
}
}
```
此時將會喚醒前面`get` 方法 while中的線程阻塞, 將結(jié)果返回槽棍。這就是一個請求響應(yīng)的過程捉蚤。
## redis 集群的幾種實現(xiàn)方式(技術(shù)總監(jiān)面)
> 這個問題回答的不是很好抬驴,只是簡單的說是通過 redisClient 來實現(xiàn)的客戶端分片,redis集群互相不感知缆巧。然后被問到當集群中有一臺掛了會有什么樣的結(jié)果時布持,就說到了一致性hash。當問掛了的數(shù)據(jù)對線上的操作造成影響時怎么辦陕悬,然后回答說通過redis一主一叢题暖。但是這個問題大體上來回回答的不是特別好。應(yīng)當明確redis實現(xiàn)集群的幾種方式(客戶端分片,redis 原生cluster等),實現(xiàn)原理墩莫,`故障應(yīng)對策略`等芙委。
##