日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

面试中又被问到Redis如何实现抢购,赶快代码实现一波吧!

發布時間:2025/3/20 数据库 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 面试中又被问到Redis如何实现抢购,赶快代码实现一波吧! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

點擊上方?好好學java?,選擇?星標?公眾號

重磅資訊、干貨,第一時間送達

今日推薦:硬剛一周,3W字總結,一年的經驗告訴你如何準備校招!

個人原創100W+訪問量博客:點擊前往,查看更多

作者:goodlook0123

https://blog.csdn.net/goodlook0123

面試常常遇到寫一個搶購實例,或者講講搶購實現想法,然后總是講不明白,因為目前工作沒做搶購這一塊兒。

但是這個想法今天終于搞明白了,其中也參照了一些大佬的做法。

這篇文章直接使用redis,其中注釋也寫的挺明白的,直接上代碼:

junit測試類:

Log?log?=?LogFactory.getLog(getClass());@Autowired??private?RedisTemplate<String,?Object>?redisTemplate;??@Testpublic?void?testSys()?throws?Exception{log.info("開始");MsService?service?=?new?MsService();for?(int?i?=?0;?i?<?10;?i++)?{ThreadB?threadA?=?new?ThreadB(service,?redisTemplate,?"MSKEY");threadA.start();log.info("*******************結束");}}

threadB類:

private?MsService?service;private?RedisTemplate<String,Object>?redisTemplate;private?String?key;public?ThreadB(MsService?service,RedisTemplate<String,Object>?redisTemplate,String?key)?{this.service?=?service;this.redisTemplate=redisTemplate;this.key=key;}@Overridepublic?void?run()?{try?{service.seckill(redisTemplate,?key);}?catch?(InterruptedException?e)?{//?TODO?Auto-generated?catch?blocke.printStackTrace();}?catch?(Exception?e)?{//?TODO?Auto-generated?catch?blocke.printStackTrace();}}

msService類:

Log?log?=?LogFactory.getLog(getClass());/****?搶購代碼*?@param?redisTemplate*?@param?key?pronum?首先用客戶端設置數量*?@return*?@throws?InterruptedException?*/public?boolean?seckill(RedisTemplate<String,Object>?redisTemplate,?String?key)?throws?Exception?{RedisLock?lock?=?new?RedisLock(redisTemplate,?key,?10000,?20000);try?{if?(lock.lock())?{//?需要加鎖的代碼String?pronum=lock.get("pronum");//修改庫存if(Integer.parseInt(pronum)-1>=0)?{lock.set("pronum",String.valueOf(Integer.parseInt(pronum)-1));log.info("庫存數量:"+pronum+"?????成功!!!"+Thread.currentThread().getName());}else?{log.info("已經被搶光了,請參與下輪搶購");}log.info("++++++++++++++++++++++++++++++++++++++參加了搶購");return?true;}?}?catch?(InterruptedException?e)?{e.printStackTrace();}?finally?{//?為了讓分布式鎖的算法更穩鍵些,持有鎖的客戶端在解鎖之前應該再檢查一次自己的鎖是否已經超時,再去做DEL操作,因為可能客戶端因為某個耗時的操作而掛起,//?操作完的時候鎖因為超時已經被別人獲得,這時就不必解鎖了。?————這里沒有做lock.unlock();}return?false;}

redisLock類:

public?class?RedisLock?{private?static?Logger?logger?=?LoggerFactory.getLogger(RedisLock.class);private?RedisTemplate<String,Object>?redisTemplate;private?static?final?int?DEFAULT_ACQUIRY_RESOLUTION_MILLIS?=?100;/***?Lock?key?path.*/private?String?lockKey;/***?鎖超時時間,防止線程在入鎖以后,無限的執行等待*/private?int?expireMsecs?=?60?*?1000;/***?鎖等待時間,防止線程饑餓*/private?int?timeoutMsecs?=?10?*?1000;private?volatile?boolean?locked?=?false;/***?Detailed?constructor?with?default?acquire?timeout?10000?msecs?and?lock*?expiration?of?60000?msecs.**?@param?lockKey*????????????lock?key?(ex.?account:1,?...)*/public?RedisLock(RedisTemplate<String,Object>?redisTemplate,?String?lockKey)?{this.redisTemplate?=?redisTemplate;this.lockKey?=?lockKey?+?"_lock";}/***?Detailed?constructor?with?default?lock?expiration?of?60000?msecs.**/public?RedisLock(RedisTemplate<String,Object>?redisTemplate,?String?lockKey,?int?timeoutMsecs)?{this(redisTemplate,?lockKey);this.timeoutMsecs?=?timeoutMsecs;}/***?Detailed?constructor.**/public?RedisLock(RedisTemplate<String,Object>?redisTemplate,?String?lockKey,?int?timeoutMsecs,?int?expireMsecs)?{this(redisTemplate,?lockKey,?timeoutMsecs);this.expireMsecs?=?expireMsecs;}/***?@return?lock?key*/public?String?getLockKey()?{return?lockKey;}public?String?get(final?String?key)?{Object?obj?=?null;try?{obj?=?redisTemplate.execute(new?RedisCallback<Object>()?{@Overridepublic?Object?doInRedis(RedisConnection?connection)?throws?DataAccessException?{StringRedisSerializer?serializer?=?new?StringRedisSerializer();byte[]?data?=?connection.get(serializer.serialize(key));connection.close();if?(data?==?null)?{return?null;}return?serializer.deserialize(data);}});}?catch?(Exception?e)?{logger.error("get?redis?error,?key?:?{}",?key);}return?obj?!=?null???obj.toString()?:?null;}public?String?set(final?String?key,final?String?value)?{Object?obj?=?null;try?{obj?=?redisTemplate.execute(new?RedisCallback<Object>()?{@Overridepublic?Object?doInRedis(RedisConnection?connection)?throws?DataAccessException?{StringRedisSerializer?serializer?=?new?StringRedisSerializer();connection.set(serializer.serialize(key),?serializer.serialize(value));return?serializer;}});}?catch?(Exception?e)?{logger.error("get?redis?error,?key?:?{}",?key);}return?obj?!=?null???obj.toString()?:?null;}public?boolean?setNX(final?String?key,?final?String?value)?{Object?obj?=?null;try?{obj?=?redisTemplate.execute(new?RedisCallback<Object>()?{@Overridepublic?Object?doInRedis(RedisConnection?connection)?throws?DataAccessException?{StringRedisSerializer?serializer?=?new?StringRedisSerializer();Boolean?success?=?connection.setNX(serializer.serialize(key),?serializer.serialize(value));connection.close();return?success;}});}?catch?(Exception?e)?{logger.error("setNX?redis?error,?key?:?{}",?key);}return?obj?!=?null???(Boolean)?obj?:?false;}private?String?getSet(final?String?key,?final?String?value)?{Object?obj?=?null;try?{obj?=?redisTemplate.execute(new?RedisCallback<Object>()?{@Overridepublic?Object?doInRedis(RedisConnection?connection)?throws?DataAccessException?{StringRedisSerializer?serializer?=?new?StringRedisSerializer();byte[]?ret?=?connection.getSet(serializer.serialize(key),?serializer.serialize(value));connection.close();return?serializer.deserialize(ret);}});}?catch?(Exception?e)?{logger.error("setNX?redis?error,?key?:?{}",?key);}return?obj?!=?null???(String)?obj?:?null;}/***?獲得?lock.?實現思路:?主要是使用了redis?的setnx命令,緩存了鎖.?reids緩存的key是鎖的key,所有的共享,*?value是鎖的到期時間(注意:這里把過期時間放在value了,沒有時間上設置其超時時間)?執行過程:*?1.通過setnx嘗試設置某個key的值,成功(當前沒有這個鎖)則返回,成功獲得鎖*?2.鎖已經存在則獲取鎖的到期時間,和當前時間比較,超時的話,則設置新的值**?@return?true?if?lock?is?acquired,?false?acquire?timeouted*?@throws?InterruptedException*?????????????in?case?of?thread?interruption*/public?synchronized?boolean?lock()?throws?InterruptedException?{int?timeout?=?timeoutMsecs;while?(timeout?>=?0)?{long?expires?=?System.currentTimeMillis()?+?expireMsecs?+?1;String?expiresStr?=?String.valueOf(expires);?//?鎖到期時間if?(this.setNX(lockKey,?expiresStr))?{//?lock?acquiredlocked?=?true;return?true;}String?currentValueStr?=?this.get(lockKey);?//?redis里的時間if?(currentValueStr?!=?null?&&?Long.parseLong(currentValueStr)?<?System.currentTimeMillis())?{//?判斷是否為空,不為空的情況下,如果被其他線程設置了值,則第二個條件判斷是過不去的//?lock?is?expiredString?oldValueStr?=?this.getSet(lockKey,?expiresStr);//?獲取上一個鎖到期時間,并設置現在的鎖到期時間,//?只有一個線程才能獲取上一個線上的設置時間,因為jedis.getSet是同步的if?(oldValueStr?!=?null?&&?oldValueStr.equals(currentValueStr))?{//?防止誤刪(覆蓋,因為key是相同的)了他人的鎖——這里達不到效果,這里值會被覆蓋,但是因為什么相差了很少的時間,所以可以接受//?[分布式的情況下]:如過這個時候,多個線程恰好都到了這里,但是只有一個線程的設置值和當前值相同,他才有權利獲取鎖//?lock?acquiredlocked?=?true;return?true;}}timeout?-=?DEFAULT_ACQUIRY_RESOLUTION_MILLIS;/**?延遲100?毫秒,?這里使用隨機時間可能會好一點,可以防止饑餓進程的出現,即,當同時到達多個進程,*?只會有一個進程獲得鎖,其他的都用同樣的頻率進行嘗試,后面有來了一些進行,也以同樣的頻率申請鎖,這將可能導致前面來的鎖得不到滿足.*?使用隨機的等待時間可以一定程度上保證公平性*/Thread.sleep(DEFAULT_ACQUIRY_RESOLUTION_MILLIS);}return?false;}/***?Acqurired?lock?release.*/public?synchronized?void?unlock()?{if?(locked)?{redisTemplate.delete(lockKey);locked?=?false;}}

至此基于redis的搶購簡單實現。大佬如果覺得有不妥的地方請指正,小弟也可以進步一點點。

推薦文章

  • 硬剛一周,3W字總結,一年的經驗告訴你如何準備校招!

  • 今年的校招,Java 好拿 offer 嗎?

  • 10月了,該聊聊今年秋招了!

  • 聊聊在騰訊實習快一個月的感受

原創電子書歷時整整一年總結的?Java 面試 + Java 后端技術學習指南,這是本人這幾年及校招的總結,各種高頻面試題已經全部進行總結,按照章節復習即可,已經拿到了大廠offer。 原創思維導圖掃碼或者微信搜?程序員的技術圈子?回復?面試?領取原創電子書和思維導圖。

總結

以上是生活随笔為你收集整理的面试中又被问到Redis如何实现抢购,赶快代码实现一波吧!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。