1 前言
本節主要記錄下基於 AQS 衍生出來的一些常用鎖比如:CountDownLatch、ReentrantLock、Semaphore、ReentrantReadWriteLock 等他們在原始碼中的一些應用,好記性不如爛筆頭。
2 CountDownLatch
2.1 RocketMQ 中 Broker 向 所有NameServer 的註冊
RocketMQ 路由註冊是透過 Broker 與 NameServer 的心跳功能實現的。 Broker啟動時向叢集中所有的NameServ巳r傳送心跳語句,每隔 30s 向叢集中所有NameServer傳送心跳包, NameServer 收到 Broker 心跳包時會更新 brokerLiveTable 快取中 BrokerLivelnfo 的 lastUpdateTimestamp ,然後 Nam巳 Server 每隔 10s 掃描 brokerLiveTable,如果連續 !20s 沒有收到心跳包, NameServer將移除該Broker 的路由資訊同時關閉 Socket連線。
那麼這裡比如我有 4個 NameServer,是迴圈遍歷一個一個註冊麼?其實不是,這裡就用到了執行緒池 + CountDownLatch,我們看下:
public List<RegisterBrokerResult> registerBrokerAll( final String clusterName, final String brokerAddr, final String brokerName, final long brokerId, final String haServerAddr, final TopicConfigSerializeWrapper topicConfigWrapper, final List<String> filterServerList, final boolean oneway, final int timeoutMills, final boolean enableActingMaster, final boolean compressed, final Long heartbeatTimeoutMillis, final BrokerIdentity brokerIdentity) { final List<RegisterBrokerResult> registerBrokerResultList = new CopyOnWriteArrayList<>(); List<String> nameServerAddressList = this.remotingClient.getAvailableNameSrvList(); if (nameServerAddressList != null && nameServerAddressList.size() > 0) { final RegisterBrokerRequestHeader requestHeader = new RegisterBrokerRequestHeader(); requestHeader.setBrokerAddr(brokerAddr); requestHeader.setBrokerId(brokerId); requestHeader.setBrokerName(brokerName); requestHeader.setClusterName(clusterName); requestHeader.setHaServerAddr(haServerAddr); requestHeader.setEnableActingMaster(enableActingMaster); requestHeader.setCompressed(false); if (heartbeatTimeoutMillis != null) { requestHeader.setHeartbeatTimeoutMillis(heartbeatTimeoutMillis); } RegisterBrokerBody requestBody = new RegisterBrokerBody(); requestBody.setTopicConfigSerializeWrapper(TopicConfigAndMappingSerializeWrapper.from(topicConfigWrapper)); requestBody.setFilterServerList(filterServerList); final byte[] body = requestBody.encode(compressed); final int bodyCrc32 = UtilAll.crc32(body); requestHeader.setBodyCrc32(bodyCrc32); // 建立一個計數器鎖(數量為 nameServer 的個數) final CountDownLatch countDownLatch = new CountDownLatch(nameServerAddressList.size()); // 迴圈遍歷 for (final String namesrvAddr : nameServerAddressList) { // 往執行緒池中扔任務 brokerOuterExecutor.execute(new AbstractBrokerRunnable(brokerIdentity) { @Override public void run0() { try { RegisterBrokerResult result = registerBroker(namesrvAddr, oneway, timeoutMills, requestHeader, body); if (result != null) { registerBrokerResultList.add(result); } LOGGER.info("Registering current broker to name server completed. TargetHost={}", namesrvAddr); } catch (Exception e) { LOGGER.error("Failed to register current broker to name server. TargetHost={}", namesrvAddr, e); } finally { // 計數器減1 countDownLatch.countDown(); } } }); } try { // 超時等待都註冊完畢 if (!countDownLatch.await(timeoutMills, TimeUnit.MILLISECONDS)) { LOGGER.warn("Registration to one or more name servers does NOT complete within deadline. Timeout threshold: {}ms", timeoutMills); } } catch (InterruptedException ignore) { } } return registerBrokerResultList; }
可以看到是透過遍歷,向執行緒池中執行任務,每個執行緒註冊完會讓鎖減1,最後下邊 await 等待所有的執行緒都註冊完畢。
3 ReentrantLock
4 Semaphore
5 ReentrantReadWriteLock
6 小結
歸納總結是對技術的鞏固以及認識的增強,看看人家別人怎麼用的,什麼場景下用的,用法上跟自己的有什麼不同,甚至在自己寫業務的時候,是不是能直接借鑑下,哈哈哈,受益頗多都,所以大家也要學會總結積累哈。本節就主要記錄下鎖的一些原始碼應用(會持續增加),有理解不對的地方歡迎指正哈。