婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > 利用redis實現分布式鎖,快速解決高并發時的線程安全問題

利用redis實現分布式鎖,快速解決高并發時的線程安全問題

熱門標簽:北京400電話辦理收費標準 鄭州人工智能電銷機器人系統 十堰營銷電銷機器人哪家便宜 山東外呼銷售系統招商 宿遷便宜外呼系統平臺 日本中國地圖標注 魔獸2青云地圖標注 貴州電銷卡外呼系統 超呼電話機器人

實際工作中,經常會遇到多線程并發時的類似搶購的功能,本篇描述一個簡單的redis分布式鎖實現的多線程搶票功能。

直接上代碼。首先按照慣例,給出一個錯誤的示范:

我們可以看看,當20個線程一起來搶10張票的時候,會發生什么事。

package com.tiger.utils; 
public class TestMutilThread {
 
	// 總票量
	public static int count = 10; 
	public static void main(String[] args) {
		statrtMulti();
	}
 
	public static void statrtMulti() {
		for (int i = 1; i = 20; i++) {
			TicketRunnable tickrunner = new TicketRunnable();
			Thread thread = new Thread(tickrunner, "Thread No: " + i);
			thread.start();
		} 
	}
 
	public static class TicketRunnable implements Runnable {
 
		@Override
		public void run() {
			System.out.println(Thread.currentThread().getName() + " start "
					+ count);
			// TODO Auto-generated method stub
			// logger.info(Thread.currentThread().getName()
			// + " really start" + count);
			if (count = 0) {
				System.out.println(Thread.currentThread().getName()
						+ " ticket sold out ! No tickets remained!" + count);
				return;
			} else {
				count = count - 1;
				System.out.println(Thread.currentThread().getName()
						+ " bought a ticket,now remaining :" + (count));
			}
		}
	}
}

測試結果,從結果可以看到,票數在不同的線程中已經出現混亂。

Thread No: 2 start 10
Thread No: 6 start 10
Thread No: 4 start 10
Thread No: 5 start 10
Thread No: 3 start 10
Thread No: 9 start 6
Thread No: 1 start 10
Thread No: 1 bought a ticket,now remaining :3
Thread No: 9 bought a ticket,now remaining :4
Thread No: 3 bought a ticket,now remaining :5
Thread No: 12 start 3
Thread No: 5 bought a ticket,now remaining :6
Thread No: 4 bought a ticket,now remaining :7
Thread No: 8 start 7
Thread No: 7 start 8
Thread No: 12 bought a ticket,now remaining :1
Thread No: 14 start 0
Thread No: 6 bought a ticket,now remaining :8
Thread No: 16 start 0
Thread No: 2 bought a ticket,now remaining :9
Thread No: 16 ticket sold out ! No tickets remained!0
Thread No: 14 ticket sold out ! No tickets remained!0
Thread No: 18 start 0
Thread No: 18 ticket sold out ! No tickets remained!0
Thread No: 7 bought a ticket,now remaining :0
Thread No: 15 start 0
Thread No: 8 bought a ticket,now remaining :1
Thread No: 13 start 2
Thread No: 19 start 0
Thread No: 11 start 3
Thread No: 11 ticket sold out ! No tickets remained!0
Thread No: 10 start 3
Thread No: 10 ticket sold out ! No tickets remained!0
Thread No: 19 ticket sold out ! No tickets remained!0
Thread No: 13 ticket sold out ! No tickets remained!0
Thread No: 20 start 0
Thread No: 20 ticket sold out ! No tickets remained!0
Thread No: 15 ticket sold out ! No tickets remained!0
Thread No: 17 start 0
Thread No: 17 ticket sold out ! No tickets remained!0

為了解決多線程時出現的混亂問題,這里給出真正的測試類!!!

真正的測試類,這里啟動20個線程,來搶10張票。

RedisTemplate 是用來實現redis操作的,由spring進行集成。這里是使用到了RedisTemplate,所以我以構造器的形式在外部將RedisTemplate傳入到測試類中。

MultiTestLock 是用來實現加鎖的工具類。

總票數使用volatile關鍵字,實現多線程時變量在系統內存中的可見性,這點可以去了解下volatile關鍵字的作用。

TicketRunnable用于模擬搶票功能。

其中由于lock與unlock之間存在if判斷,為保證線程安全,這里使用synchronized來保證。

測試類:

package com.tiger.utils; 
import java.io.Serializable; 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate; 
public class MultiConsumer {
	Logger logger=LoggerFactory.getLogger(MultiTestLock.class);	
	private RedisTemplateSerializable, Serializable> redisTemplate;	
	public MultiTestLock lock;
	//總票量
	public volatile static int count = 10;
 
	public void statrtMulti() {
		lock = new MultiTestLock(redisTemplate);
		for (int i = 1; i = 20; i++) {
			TicketRunnable tickrunner = new TicketRunnable();
			Thread thread = new Thread(tickrunner, "Thread No: " + i);
			thread.start();
			} 
	}
 
	public class TicketRunnable implements Runnable {
 
		@Override
		public void run() {
			logger.info(Thread.currentThread().getName() + " start "
					+ count);
			// TODO Auto-generated method stub
			if (count > 0) {
//				logger.info(Thread.currentThread().getName()
//						+ " really start" + count);
				lock.lock();
				synchronized (this) {
					if(count=0){
						logger.info(Thread.currentThread().getName()
								+ " ticket sold out ! No tickets remained!" + count);
						lock.unlock();
						return;
					}else{
						count=count-1;
						logger.info(Thread.currentThread().getName()
								+ " bought a ticket,now remaining :" + (count));
					}
				}
				lock.unlock();
			}else{
				logger.info(Thread.currentThread().getName()
						+ " ticket sold out !" + count);
			}
		}
	}
 
	public RedisTemplateSerializable, Serializable> getRedisTemplate() {
		return redisTemplate;
	}
 
	public void setRedisTemplate(
			RedisTemplateSerializable, Serializable> redisTemplate) {
		this.redisTemplate = redisTemplate;
	}
 
	public MultiConsumer(RedisTemplateSerializable, Serializable> redisTemplate) {
		super();
		this.redisTemplate = redisTemplate;
	}
}

Lock工具類:

我們知道為保證線程安全,程序中執行的操作必須時原子的。redis后續的版本中可以使用set key同時設置expire超時時間。

想起上次去 電信翼支付 面試時,面試官問過一個問題:分布式鎖如何防止死鎖,問題關鍵在于我們在分布式中進行加鎖操作時成功了,但是后續業務操作完畢執行解鎖時出現失敗。導致分布式鎖無法釋放。出現死鎖,后續的加鎖無法正常進行。所以這里設置expire超時時間的目的就是防止出現解鎖失敗的情況,這樣,即使解鎖失敗了,分布式鎖依然會在超時時間過了之后自動釋放。

具體在代碼中也有注釋,也可以作為參考。

package com.tiger.utils; 
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock; 
import javax.sound.midi.MidiDevice.Info; 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.script.RedisScript; 
 
public class MultiTestLock implements Lock {	
	Logger logger=LoggerFactory.getLogger(MultiTestLock.class);	
	private RedisTemplateSerializable, Serializable> redisTemplate;	
	public MultiTestLock(RedisTemplateSerializable, Serializable> redisTemplate) {
		super();
		this.redisTemplate = redisTemplate;
	}
 
	@Override
	public void lock() {
		//這里使用while循環強制線程進來之后先進行搶鎖操作。只有搶到鎖才能進行后續操作
		while(true){
			if(tryLock()){
				try {
					//這里讓線程睡500毫秒的目的是為了模擬業務耗時,確保業務結束時之前設置的值正好打到超時時間,
					//實際生產中可能有偏差,這里需要經驗
					Thread.sleep(500l);
//					logger.info(Thread.currentThread().getName()+" time to awake");
					return;
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}else{
				try {
					//這里設置一個隨機毫秒的sleep目的時降低while循環的頻率 
					Thread.sleep(new Random().nextInt(200)+100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
 
	@Override
	public boolean tryLock() {
		//這里也可以選用transactionSupport支持事務操作
		SessionCallbackObject> sessionCallback=new SessionCallbackObject>() {
			@Override
			public Object execute(RedisOperations operations)
					throws DataAccessException {
				operations.multi();
				operations.opsForValue().setIfAbsent("secret", "answer");
				//設置超時時間要根據業務實際的可能處理時間來,是一個經驗值
				operations.expire("secret", 500l, TimeUnit.MILLISECONDS);
				Object object=operations.exec();
				return object;
			}
		};
		//執行兩部操作,這里會拿到一個數組值 [true,true],分別對應上述兩部操作的結果,如果中途出現第一次為false則表明第一步set值出錯
		ListBoolean> result=(List) redisTemplate.execute(sessionCallback);
//		logger.info(Thread.currentThread().getName()+" try lock "+ result);
		if(true==result.get(0)||"true".equals(result.get(0)+"")){
			logger.info(Thread.currentThread().getName()+" try lock success");
			return true;
		}else{
			return false;
		}
	}
 
	@Override
	public boolean tryLock(long arg0, TimeUnit arg1)
			throws InterruptedException {
		// TODO Auto-generated method stub
		return false;
	}
 
	@Override
	public void unlock() {
		//unlock操作直接刪除鎖,如果執行完還沒有達到超時時間則直接刪除,讓后續的線程進行繼續操作。起到補刀的作用,確保鎖已經超時或被刪除
		SessionCallbackObject> sessionCallback=new SessionCallbackObject>() {
			@Override
			public Object execute(RedisOperations operations)
					throws DataAccessException {
				operations.multi();
				operations.delete("secret");
				Object object=operations.exec();
				return object;
			}
		};
		Object result=redisTemplate.execute(sessionCallback);
	} 
 
	@Override
	public void lockInterruptibly() throws InterruptedException {
		// TODO Auto-generated method stub
	}
 
	@Override
	public Condition newCondition() {
		// TODO Auto-generated method stub
		return null;
	}
	
	public RedisTemplateSerializable, Serializable> getRedisTemplate() {
		return redisTemplate;
	}
 
	public void setRedisTemplate(
			RedisTemplateSerializable, Serializable> redisTemplate) {
		this.redisTemplate = redisTemplate;
	}
}

執行結果

可以看到,票數穩步減少,后續沒有搶到鎖的線程余票為0,無票可搶。

tips:

這其中也出現了一個問題,redis進行多部封裝操作時,系統報錯:ERR EXEC without MULTI

后經過查閱發現問題出在:

在spring中,多次執行MULTI命令不會報錯,因為第一次執行時,會將其內部的一個isInMulti變量設為true,后續每次執行命令是都會檢查這個變量,如果為true,則不執行命令。

而多次執行EXEC命令則會報開頭說的"ERR EXEC without MULTI"錯誤。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。

您可能感興趣的文章:
  • 詳解redis分布式鎖的這些坑
  • 基于Redis實現分布式鎖的方法(lua腳本版)
  • SpringBoot之使用Redis實現分布式鎖(秒殺系統)
  • 詳解Redis 分布式鎖遇到的序列化問題
  • 詳解RedisTemplate下Redis分布式鎖引發的系列問題
  • redisson分布式鎖的用法大全
  • php基于redis的分布式鎖實例詳解
  • Redis分布式鎖升級版RedLock及SpringBoot實現方法
  • 詳解基于redis實現分布式鎖

標簽:北京 吉安 大慶 楊凌 江蘇 朝陽 臺州 果洛

巨人網絡通訊聲明:本文標題《利用redis實現分布式鎖,快速解決高并發時的線程安全問題》,本文關鍵詞  利用,redis,實現,分布式,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《利用redis實現分布式鎖,快速解決高并發時的線程安全問題》相關的同類信息!
  • 本頁收集關于利用redis實現分布式鎖,快速解決高并發時的線程安全問題的相關信息資訊供網民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    国产精品天美传媒沈樵| 91理论电影在线观看| 一区二区三区在线播| 337p亚洲精品色噜噜狠狠| 久久综合狠狠综合久久激情| 国产v综合v亚洲欧| 夜夜爽夜夜爽精品视频| 欧美激情在线一区二区| 亚洲a一区二区| 激情成人综合网| 久久精品国产一区二区三| 国产精品自拍av| 在线国产亚洲欧美| 欧美丝袜自拍制服另类| 精品视频色一区| 久久综合久久鬼色| 国产精品国产a级| 亚洲欧洲制服丝袜| 色婷婷久久久亚洲一区二区三区| 国产精品123| 91偷拍与自偷拍精品| 91精品蜜臀在线一区尤物| 欧美一区二区啪啪| 欧美本精品男人aⅴ天堂| 国产精品乱码妇女bbbb| 亚洲精品国产第一综合99久久 | 欧美一区二区三区爱爱| 久久这里都是精品| 亚洲午夜在线电影| 国产精品不卡一区| 国内成人免费视频| 欧美精品一卡二卡| 亚洲成人黄色小说| 欧美成人一区二区三区在线观看 | 国产精品一二一区| 欧美午夜精品免费| 中文字幕一区二区三区精华液| 奇米色777欧美一区二区| 欧美久久久久久蜜桃| 国产精品久久影院| 99久久免费视频.com| 亚洲精品久久久蜜桃| 色狠狠桃花综合| 青青草成人在线观看| 日韩精品一区国产麻豆| 久久草av在线| 最新国产の精品合集bt伙计| 国产剧情一区在线| 亚洲国产精品ⅴa在线观看| 成人激情午夜影院| 亚洲国产精品ⅴa在线观看| 成人一级片在线观看| 国产精品久久久久久久久果冻传媒 | 国产中文字幕一区| 精品国产凹凸成av人网站| 日韩电影在线免费看| 色婷婷久久综合| 色8久久精品久久久久久蜜| 亚洲午夜在线视频| 欧美性感一类影片在线播放| 亚洲精品乱码久久久久久日本蜜臀| 国产91精品久久久久久久网曝门| 久久九九99视频| 91网站最新地址| 亚洲一区二区在线免费看| 欧美日韩亚洲综合| 国产在线乱码一区二区三区| 国产精品色一区二区三区| 91黄色小视频| 亚洲精品高清在线| 日韩欧美国产综合在线一区二区三区| 午夜影视日本亚洲欧洲精品| 精品精品欲导航| 91丨porny丨中文| 天天综合网 天天综合色| 欧美美女bb生活片| av福利精品导航| 国产精品视频麻豆| 日韩视频免费观看高清完整版在线观看| 毛片一区二区三区| 亚洲欧美日韩久久| 精品美女被调教视频大全网站| www.亚洲精品| 国模一区二区三区白浆| 香蕉成人啪国产精品视频综合网| 538在线一区二区精品国产| 国产河南妇女毛片精品久久久| 亚洲一级在线观看| 综合欧美亚洲日本| 国产蜜臀av在线一区二区三区| 2023国产精品| 精品成人免费观看| 久久综合丝袜日本网| 欧美大片一区二区| 欧美一区二区三区在线看| 欧美日韩电影在线播放| 成人av电影在线观看| 亚洲狠狠爱一区二区三区| 日韩vs国产vs欧美| 男人操女人的视频在线观看欧美| 亚洲宅男天堂在线观看无病毒| 亚洲欧洲中文日韩久久av乱码| 综合久久给合久久狠狠狠97色| 国产精品久久久久久久第一福利 | 成人午夜电影小说| 国产成人av福利| 94-欧美-setu| 欧美日韩国产经典色站一区二区三区| 黄色小说综合网站| 欧美揉bbbbb揉bbbbb| 欧美成人精品福利| 亚洲免费看黄网站| 韩国一区二区三区| 97国产一区二区| 精品久久久久久久久久久久包黑料| 中文字幕中文字幕中文字幕亚洲无线| 亚洲欧美激情小说另类| 日本女优在线视频一区二区| 国产高清久久久久| 精品视频在线看| 一区二区三区中文在线| 国产蜜臀av在线一区二区三区| 激情六月婷婷综合| 欧美一区二区日韩一区二区| 国产亚洲精品中文字幕| 亚洲福利视频三区| 91日韩精品一区| 欧美日韩国产首页| 日韩欧美国产高清| 青青草国产精品亚洲专区无| 99久久精品99国产精品| 久久精品网站免费观看| 日韩精品电影在线| 欧美亚洲国产一卡| 亚洲午夜精品17c| 欧美日韩精品三区| 国产·精品毛片| 欧美国产激情一区二区三区蜜月| 九一九一国产精品| 91精品国产麻豆| 国产精品自在在线| 玉米视频成人免费看| 91丝袜国产在线播放| 一区二区三区欧美日| 91在线码无精品| 一色屋精品亚洲香蕉网站| 欧美亚洲动漫另类| 香蕉久久一区二区不卡无毒影院| 欧美久久久久久蜜桃| 另类调教123区| 日韩视频免费直播| 成人黄页毛片网站| 美女国产一区二区| 首页欧美精品中文字幕| 日韩免费电影一区| 成人国产精品免费网站| 视频一区二区中文字幕| 国产网红主播福利一区二区| 天天色天天爱天天射综合| 精品国产乱码久久久久久浪潮| www.欧美日韩| 久久91精品国产91久久小草| 亚洲三级在线免费| 国产欧美在线观看一区| 91精品国产综合久久久久久久久久| 国产一区二区三区四区五区美女 | 一区二区三区欧美在线观看| 精品美女一区二区| 欧美大黄免费观看| 91久久免费观看| 91丨porny丨户外露出| 国产成人av电影在线观看| 捆绑调教美女网站视频一区| 日韩在线观看一区二区| 欧美高清在线一区| 日韩一区二区三区电影| 在线精品视频免费播放| 91一区二区三区在线播放| 成人综合日日夜夜| 天天操天天干天天综合网| 一区二区欧美在线观看| 国产精品第四页| 亚洲一区在线观看免费观看电影高清| 最新日韩在线视频| 亚洲最新视频在线播放| 日韩精品一级中文字幕精品视频免费观看| 亚洲一区二区三区四区的| 国产三级精品三级| 亚洲人成人一区二区在线观看| 尤物av一区二区| 麻豆91在线播放免费| 亚洲成人动漫在线免费观看| 美女一区二区久久| 青青国产91久久久久久| 国产成人av电影在线播放| 成人高清视频免费观看| 在线精品视频一区二区三四| 日韩亚洲欧美在线| 国产精品国产三级国产aⅴ中文 | 亚洲黄色性网站|