溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

String中怎么實現(xiàn)一個同步鎖

發(fā)布時間:2021-08-06 17:19:25 來源:億速云 閱讀:116 作者:Leah 欄目:編程語言

String中怎么實現(xiàn)一個同步鎖,針對這個問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

在某些時候,我們可能想基于字符串做一些事情,比如:針對同一用戶的并發(fā)同步操作,使用鎖字符串的方式實現(xiàn)比較合理。因為只有在相同字符串的情況下,并發(fā)操作才是不被允許的。而如果我們不分青紅皂白直接全部加鎖,那么整體性能就下降得厲害了。

因為string的多樣性,看起來string鎖是天然比分段鎖之類的高級鎖更有優(yōu)勢呢。

因為String 類型的變量賦值是這樣的: String a = "hello world."; 所有往往會有個錯誤的映象,String對象就是不可變的。

額,關(guān)于這個問題的爭論咱們就不細(xì)說了,總之, "a" != "a" 是有可能成立的。

另外,針對上鎖這件事,我們都知道,鎖是要針對同一個對象,才會有意義。所以,粗略的,我們可以這樣使用字符串鎖:

public void method1() {    String str1 = "a";    synchronized (str1) {      // do sync a things...    }  }      public void method2() {    String str2 = "a";    synchronized (str2) {      // do sync b things...    }  }

乍一看,這的確很方便簡單。但是,前面說了, "a" 是可能不等于 "a" 的(這是大部分情況,只有當(dāng)String被存儲在常量池中時值相同的String變量才相等)。

所以,我們可以稍微優(yōu)化下:

public void method3() {    String str1 = "a";    synchronized (str1.intern()) {      // do sync a things...    }  }  public void method4() {    String str2 = "a";    synchronized (str2.intern()) {      // do sync b things...    }  }

看起來還是很方便簡單的,其原理就是把String對象放到常量池中。但是會有個問題,這些常量池的數(shù)據(jù)如何清理呢?

不管怎么樣,我們是不是可以自己去基于String實現(xiàn)一個鎖呢?

肯定是可以的了!直接上代碼!

import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ConcurrentMap;import java.util.concurrent.CountDownLatch;/** * 基于string 的鎖實現(xiàn) */public final class StringBasedMutexLock {  private static final Logger logger = LoggerFactory.getLogger(StringBasedMutexLock.class);  /**   * 字符鎖 管理器, 將每個字符串 轉(zhuǎn)換為一個 CountDownLatch   *   *   即鎖只會發(fā)生在真正有并發(fā)更新 同一個 String 的情況下   *   */  private static final ConcurrentMap<String, CountDownLatch> lockKeyHolder = new ConcurrentHashMap<>();  /**   * 基于lockKey 上鎖,同步執(zhí)行   *   * @param lockKey 字符鎖   */  public static void lock(String lockKey) {    while (!tryLock(lockKey)) {      try {        logger.debug("【字符鎖】并發(fā)更新鎖升級, {}", lockKey);        blockOnSecondLevelLock(lockKey);      } catch (InterruptedException e) {        Thread.currentThread().interrupt();        logger.error("【字符鎖】中斷異常:" + lockKey, e);        break;      }    }  }  /**   * 釋放 lockKey 對應(yīng)的鎖選項,使其他線程可執(zhí)行   *   * @param lockKey 要使用互斥的字符串   * @return true: 釋放成功, false: 釋放失敗,可能被其他線程誤釋放   */  public static boolean unlock(String lockKey) {    // 先刪除鎖,再釋放鎖,此處會導(dǎo)致后續(xù)進(jìn)來的并發(fā)優(yōu)先執(zhí)行,無影響    CountDownLatch realLock = getAndReleaseLock1(lockKey);    releaseSecondLevelLock(realLock);    return true;  }  /**   * 嘗試給指定字符串上鎖   *   * @param lockKey 要使用互斥的字符串   * @return true: 上鎖成功, false: 上鎖失敗   */  private static boolean tryLock(String lockKey) {    // 此處會導(dǎo)致大量 ReentrantLock 對象創(chuàng)建嗎?    // 其實不會的,這個數(shù)量最大等于外部并發(fā)數(shù),只是對 gc 不太友好,會反復(fù)創(chuàng)建反復(fù)銷毀y    return lockKeyHolder.putIfAbsent(lockKey, new CountDownLatch(1)) == null;  }  /**   * 釋放1級鎖(刪除) 并返回重量級鎖   *   * @param lockKey 字符鎖   * @return 真正的鎖   */  private static CountDownLatch getAndReleaseLock1(String lockKey) {    return lockKeyHolder.remove(lockKey);  }  /**   * 二級鎖鎖定(鎖升級)   *   * @param lockKey 鎖字符串   * @throws InterruptedException 中斷時拋出異常   */  private static void blockOnSecondLevelLock(String lockKey) throws InterruptedException {    CountDownLatch realLock = getRealLockByKey(lockKey);    // 為 null 說明此時鎖已被刪除, next race    if(realLock != null) {      realLock.await();    }  }  /**   * 二級鎖解鎖(如有必要)   *   * @param realLock 鎖實例   */  private static void releaseSecondLevelLock(CountDownLatch realLock) {    realLock.countDown();  }  /**   * 通過key 獲取對應(yīng)的鎖實例   *   * @param lockKey 字符串鎖   * @return 鎖實例   */  private static CountDownLatch getRealLockByKey(String lockKey) {    return lockKeyHolder.get(lockKey);  }}

使用時,只需傳入 lockKey 即可。

// 加鎖StringBasedMutexLock.lock(linkKey);// 解鎖StringBasedMutexLock.unlock(linkKey);

這樣做有什么好處嗎?

  1. 使用ConcurrentHashMap實現(xiàn)鎖獲取,性能還是不錯的;

  2. 每個字符串對應(yīng)一個鎖,使用完成后就刪除,不會導(dǎo)致內(nèi)存溢出問題;

  3. 可以作為一個外部工具使用,業(yè)務(wù)代碼接入方便,無需像 synchronized 一樣,需要整段代碼包裹起來;

  不足之處?

  1. 使用ConcurrentHashMap實現(xiàn)鎖獲取,性能還是不錯的;

  2. 每個字符串對應(yīng)一個鎖,使用完成后就刪除,不會導(dǎo)致內(nèi)存溢出問題;

  3. 可以作為一個外部工具使用,業(yè)務(wù)代碼接入方便,無需像 synchronized 一樣,需要整段代碼包裹起來;

  4. 本文只是想展示實現(xiàn) String 鎖,此鎖并不適用于分布式場景下的并發(fā)處理;

擴(kuò)展: 如果不使用 String 做鎖,如何保證大并發(fā)前提下的小概率并發(fā)場景的線程安全?

我們知道 CAS 的效率是比較高的,我們可以使用原子類來進(jìn)行CAS的操作。

比如,我們添加一狀態(tài)字段, 操作此字段以保證線程安全:

/**   * 運(yùn)行狀態(tài)   *   *     4: 正在刪除, 1: 正在放入隊列中, 0: 正常無運(yùn)行   */  private transient volatile AtomicInteger runningStatus = new AtomicInteger(0);      // 更新時先獲取該狀態(tài):  public void method5() {    AtomicInteger runningStatus = link.getRunningStatus();    // 正在刪除數(shù)據(jù)過程中,則等待    if(!runningStatus.compareAndSet(0, 1)) {      // 1. 等待另外線程刪除完成      // 2. 刪除正在更新標(biāo)識      // 3. 重新運(yùn)行本次數(shù)據(jù)放入邏輯      long lockStartTime = System.currentTimeMillis();      long maxLockTime = 10 * 1000;      while (!runningStatus.compareAndSet(0, 1)) {        if(System.currentTimeMillis() - lockStartTime > maxLockTime) {          break;        }      }      runningStatus.compareAndSet(1, 0);      throw new RuntimeException("數(shù)據(jù)正在更新,重新運(yùn)行: " + link.getLinkKey() + link);    }    try {      // do sync things    }    finally {      runningStatus.compareAndSet(1, 0);    }  }    public void method6() {    AtomicInteger runningStatus = link.getRunningStatus();    if (!runningStatus.compareAndSet(0, 4)) {      logger.error(" 數(shù)據(jù)正在更新中,不得刪除,返回 ");      return;    }    try {      // do sync things    }    catch (Exception e) {      logger.error("并發(fā)更新異常:", e);    }    finally {      runningStatus.compareAndSet(4, 0);    }  }

實際測試下來,CAS 性能是要比 synchronized 之類的鎖性能要好的。當(dāng)然,我們這里針對的并發(fā)數(shù)都是極少的,我們只是想要保證這極少情況下的線程安全性。所以,其實也還好。

關(guān)于String中怎么實現(xiàn)一個同步鎖問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI