中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

解決線程并發redisson使用時常見問題有哪些

發布時間:2021-06-18 14:08:09 來源:億速云 閱讀:809 作者:chen 欄目:開發技術

這篇文章主要講解了“解決線程并發redisson使用時常見問題有哪些”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“解決線程并發redisson使用時常見問題有哪些”吧!

線程并發redisson的坑

背景

因為業務上的一個購買需求,需要對庫存進行行程保護,防止超賣的出現(我們不是電商公司),經過調研,最終選擇使用Redission來進行控制。

主要因為Redission豐富的API,開源框架,已經被廣泛應用于實際生產環境。

問題描述

當我們使用Ression中Lock.lock()方法之后,如果存在線程并發常見情況下,會出現如下異常:

java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 9f178836-f7e1-44fe-a89d-2db52f399c0d thread-id: 22

問題分析

在thread-1還沒有結束的時候,也就是在thread-1在獲得鎖但是還沒有釋放鎖的時候, `thread-2由于被別的線程中斷停止了等待從lock.tryLock的阻塞狀態中返回繼續執行接下來的邏輯,并且由于嘗試去釋放一個屬于線程thread-1的鎖而拋出了一個運行時異常導致該線程thread-2結束了, 然而thread-2完成了一系列操作后,線程thread-1才釋放了自己的鎖.

所以thread-2并沒有獲得鎖,卻執行了需要同步的內容,還嘗試去釋放鎖。

那解決方式我們就知道了,當前線程加的鎖由當前線程去解鎖,也就是說當我們使用lock.unlock的時候加上線程的判斷即可。

問題解決

 RLock lock = redissonClient.getLock(key);
    if(lock.isLocked()){ // 是否還是鎖定狀態
      if(lock.isHeldByCurrentThread()){ // 時候是當前執行線程的鎖
        lock.unlock(); // 釋放鎖
      }
    }

redisson使用注意事項

Redisson 是一個在 Redis 的基礎上實現的 Java 駐內存數據網格,相較于暴露底層操作的Jedis,Redisson提供了一系列的分布式的 Java 常用對象,還提供了許多分布式服務。

特性 & 功能:

  • 支持 Redis 單節點(single)模式、哨兵(sentinel)模式、主從(Master/Slave)模式以及集群(Redis Cluster)模式

  • 程序接口調用方式采用異步執行和異步流執行兩種方式

  • 數據序列化,Redisson 的對象編碼類是用于將對象進行序列化和反序列化,以實現對該對象在 Redis 里的讀取和存儲

  • 單個集合數據分片,在集群模式下,Redisson 為單個 Redis 集合類型提供了自動分片的功能

  • 提供多種分布式對象,如:Object Bucket,Bitset,AtomicLong,Bloom Filter 和 HyperLogLog 等

  • 提供豐富的分布式集合,如:Map,Multimap,Set,SortedSet,List,Deque,Queue 等

  • 分布式鎖和同步器的實現,可重入鎖(Reentrant Lock),公平鎖(Fair Lock),聯鎖(MultiLock),紅鎖(Red Lock),信號量(Semaphonre),可過期性信號鎖(PermitExpirableSemaphore)等

  • 提供先進的分布式服務,如分布式遠程服務(Remote Service),分布式實時對象(Live Object)服務,分布式執行服務(Executor Service),分布式調度任務服務(Schedule Service)和分布式映射歸納服務(MapReduce)

  • 更多特性和功能,請關注官網:http://redisson.org

實現原理

redis本身是不支持上述的分布式對象和集合,Redisson是通過利用redis的特性在客戶端實現了高級數據結構和特性,例如優先隊列的實現,是通過客戶端排序整理后再存入redis。

客戶端實現,意味著當沒有任何客戶端在線時,這些所有的數據結構和特性都不會保留,也不會自動生效,例如過期事件的觸發或原來優先隊列的元素增加。

注意事項

實時性

RMap中有一個功能是可以設置鍵值對的過期時間的,并可以注冊鍵值對的事件監聽器

元素淘汰功能(Eviction)

Redisson的分布式的RMapCache Java對象在基于RMap的前提下實現了針對單個元素的淘汰機制。同時仍然保留了元素的插入順序。由于RMapCache是基于RMap實現的,使它同時繼承了java.util.concurrent.ConcurrentMap接口和java.util.Map接口。Redisson提供的Spring Cache整合以及JCache正是基于這樣的功能來實現的。

目前的Redis自身并不支持散列(Hash)當中的元素淘汰,因此所有過期元素都是通過org.redisson.EvictionScheduler實例來實現定期清理的。為了保證資源的有效利用,每次運行最多清理300個過期元素。任務的啟動時間將根據上次實際清理數量自動調整,間隔時間趨于1秒到1小時之間。比如該次清理時刪除了300條元素,那么下次執行清理的時間將在1秒以后(最小間隔時間)。一旦該次清理數量少于上次清理數量,時間間隔將增加1.5倍。

正如官方wiki所述,這個功能是通過后臺線程定時去清理的, 所以這個是非實時的(issue-1234:on expired event is not executed in real-time.),延遲在5秒到2小時之間,因此對實時性要求比較高的場景就得自己衡量了。

由于過期時間的非實時性,所以導致過期事件的發生也是非實時的,相應的監聽器可能會延遲了一會兒才收到通知,在我的測試中,ttl設置在秒級誤差是比較大的,分鐘級別的ttl倒還好(左側設置值,右側實際耗時):

1s _ 5s
3s _ 5s
4s _ 5s
5s _ 9s
6s _ 10s
10s _ 15s
1m _ 1m11s

序列化

由Redisson默認的編碼器為JsonJacksonCodec,JsonJackson在序列化有雙向引用的對象時,會出現無限循環異常。而fastjson在檢查出雙向引用后會自動用引用符$ref替換,終止循環。

在我的情況中,我序列化了一個service,這個service已被spring托管,而且和另一個service之間也相互注入了,用fastjson能 正常序列化到redis,而JsonJackson則拋出無限循環異常。

為了序列化后的內容可見,所以不用redission其他自帶的二進制編碼器,自行實現編碼器:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import org.redisson.client.codec.BaseCodec;
import org.redisson.client.protocol.Decoder;
import org.redisson.client.protocol.Encoder;
import java.io.IOException;
public class FastjsonCodec extends BaseCodec {
 private final Encoder encoder = in -> {
 ByteBuf out = ByteBufAllocator.DEFAULT.buffer();
 try {
 ByteBufOutputStream os = new ByteBufOutputStream(out);
 JSON.writeJSONString(os, in,SerializerFeature.WriteClassName);
 return os.buffer();
 } catch (IOException e) {
 out.release();
 throw e;
 } catch (Exception e) {
 out.release();
 throw new IOException(e);
 }
 };

 private final Decoder<Object> decoder = (buf, state) ->
 JSON.parseObject(new ByteBufInputStream(buf), Object.class);

 @Override
 public Decoder<Object> getValueDecoder() {
 return decoder;
 }

 @Override
 public Encoder getValueEncoder() {
 return encoder;
 }
}

訂閱發布

Redisson對訂閱發布的封裝是RTopic,這也是Redisson中很多事件監聽的實現原理(例如鍵值對的事件監聽)。

使用單元測試時發現,在事件發布后,訂閱方需要延時一下才能收到事件。具體原因待查。

感謝各位的閱讀,以上就是“解決線程并發redisson使用時常見問題有哪些”的內容了,經過本文的學習后,相信大家對解決線程并發redisson使用時常見問題有哪些這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

呈贡县| 龙江县| 贵阳市| 会泽县| 九寨沟县| 札达县| 井研县| 郯城县| 富阳市| 广平县| 汶川县| 获嘉县| 岫岩| 集贤县| 惠州市| 综艺| 普宁市| 大庆市| 湘潭县| 瑞丽市| 靖州| 五台县| 盘山县| 富裕县| 泾源县| 南开区| 嘉峪关市| 资溪县| 曲阜市| 榆林市| 双柏县| 梓潼县| 寿宁县| 庆安县| 伊吾县| 谢通门县| 灵川县| 淄博市| 金山区| 青海省| 孝昌县|