0

0

redis 乐观锁实践秒杀

高洛峰

高洛峰

发布时间:2016-11-22 17:51:11

|

2870人浏览过

|

来源于php中文网

原创

需求:有一个标(理解成抢红包也行,accountbalance预赋值1000元),大家可以抢购,每个用户抢购成功后,更新最后标的总数,在并发情况下,使用redis的乐观锁,保证更新标总值正确性,先往redis放一个标的金额:

set accountBalance "1000"

实现方式如下:

pom.xml


	4.0.0

	mybatisPage
	page
	1.0-SNAPSHOT
	war

	PageHelperSample
	http://git.oschina.net/free/Mybatis-Sample

	
		UTF-8
	

	
		
		
			jstl
			jstl
			1.1.2
		
		
			taglibs
			standard
			1.1.2
		
		
		
			commons-pool
			commons-pool
			1.6
		

		
		
			org.slf4j
			slf4j-log4j12
			1.7.5
		
		
			org.slf4j
			slf4j-api
			1.7.5
		
		

		
		
			com.alibaba
			fastjson
			1.2.5
		
		
			com.alibaba
			fastjson
			1.2.4
		
		
		
			javax.servlet
			servlet-api
			2.5
			provided
		
		
			javax.servlet.jsp
			jsp-api
			2.1
			provided
		
		
			taglibs
			standard
			1.1.2
		
		
			javax.servlet
			jstl
			1.2
		

		
			com.github.pagehelper
			pagehelper
			3.7.4
		
		
			com.github.jsqlparser
			jsqlparser
			0.9.1
		

		
			junit
			junit
			4.11
			test
		
		
			log4j
			log4j
			1.2.17
		
		
			org.mybatis
			mybatis
			3.2.5
		
		
		
			org.apache.commons
			commons-lang3
			3.1
		
		
		
			mysql
			mysql-connector-java
			5.1.35
		
		
		
			redis.clients
			jedis
			2.1.0
			jar
		
	
	
		
			
				org.apache.maven.plugins
				maven-compiler-plugin
				
					1.6
					1.6
					utf-8
				
			
			
				org.mortbay.jetty
				jetty-maven-plugin
				8.0.0.M3
			
		
	

web.xml


	
		index.jsp
	
	
	
		bid
		com.heli.mybatis.page.servlet.ReidsMatchServlet
	
	
		bid
		/bid
	
	
		list
		com.heli.mybatis.page.servlet.ReidsMatchListServlet
	
	
		list
		/list
	

servlet

package com.heli.mybatis.page.servlet;

import java.io.IOException;
import java.util.List;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;

import com.commnon.RedisAPI;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;

public class ReidsMatchServlet extends HttpServlet {
	public static JedisPool pool = RedisAPI.getPool();

	// RedisAPI.set("accountBalance", "999999999");// 标还剩999999999块钱

	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		Jedis jedis = pool.getResource();
		long start = System.currentTimeMillis();
		int flag = 0;
		try {
			flag = bid(request, response, jedis);
		} catch (Exception e) {
			e.printStackTrace();
			response.getWriter().write("fail buy");
		} finally {
			pool.returnBrokenResource(jedis);
			RedisAPI.returnResource(pool, jedis);
		}
		if (flag == 1) {
			response.getWriter().write("success buy");
		} else if (flag == 2) {
			response.getWriter().write("have buy");
		} else if (flag == 0) {
			response.getWriter().write("bid is zero ,you can not buy");
		}else{
			response.getWriter().write("fail buy");
		}
		long end = System.currentTimeMillis();
		System.out.println("--------------------------------------------请求耗时:" + (end - start) + "毫秒");
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

	private int bid(HttpServletRequest request, HttpServletResponse response, Jedis jedis) throws Exception {
		int flag = 0;// 1,成功,2已经购买,3已经没钱了,其他異常
		// 每个请求对应一个userId
		int userId = new Random().nextInt(999999);
		
		// 观察 总标值,每人抢购一元
		while ("OK".equals(jedis.watch("accountBalance"))) {
        		// 判断是否购买过
        		Boolean isBuy = RedisAPI.sismember("userIdSet", userId + "");
        		if (isBuy) {
        			flag = 2;
        			return flag;
        		}
        		//投资额
			int r = 1;// new Random().nextInt(2);
			int lastAccount = 0;
			String balance = RedisAPI.get("accountBalance");
			if (StringUtils.isNotBlank(balance)) {
				lastAccount = Integer.valueOf(balance) - r;
			}
			if (lastAccount < 0) {
				flag = 3;
				break;
			}
			Transaction tx = jedis.multi();
			tx.set("accountBalance", lastAccount + "");
			List result = tx.exec();
			if (result == null || result.isEmpty()) {
				jedis.unwatch();
			} else {
				System.out.println("恭喜您," + userId + "已经中标" + r + "元,标余额" + lastAccount + "元");
				RedisAPI.set(Thread.currentThread().getName(), r + "");
				RedisAPI.sadd("userIdSet", userId + "");
				flag = 1;
				break;
			}
		}
		return flag;
	}
}
package com.heli.mybatis.page.servlet;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.commnon.RedisAPI;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class ReidsMatchListServlet extends HttpServlet {
	public static JedisPool pool= RedisAPI.getPool();;
	public static Jedis jedis;
	static {
		jedis = pool.getResource();
	}

	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		list(request, response);
		try {
			response.sendRedirect("list.jsp");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

	private void list(HttpServletRequest request, HttpServletResponse response) {
		Set set = jedis.smembers("userIdSet");
		Iterator ite = set.iterator();
		System.out.println("中标名单-------------------------");
		int i = 0;
		Map map = new HashMap();
		while (ite.hasNext()) {
			i++;
			Object obj1 = ite.next();
			System.out.println("第" + i + "名:" + obj1);
			map.put("第" + i + "名:", obj1 + "");
		}
		request.getSession().setAttribute("user", map);
		System.out.println("中标名单-------------------------");
	}

}

工具类

Pi智能演示文档
Pi智能演示文档

领先的AI PPT生成工具

下载
package com.commnon;

import java.util.ResourceBundle;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Redis操作接口
 *
 * @author 林计钦
 * @version 1.0 2013-6-14 上午08:54:14
 */
public class RedisAPI {
	private static JedisPool pool = null;
	private static ThreadLocal poolThreadLocal = new ThreadLocal();

	/**
	 * 构建redis连接池
	 * 
	 * @param ip
	 * @param port
	 * @return JedisPool
	 */
	public static JedisPool getPool() {
		if (pool == null) {
			ResourceBundle bundle = ResourceBundle.getBundle("redis");
			if (bundle == null) {
				throw new IllegalArgumentException(
						"[redis.properties] is not found!");
			}
			JedisPoolConfig config = new JedisPoolConfig();
			config.setMaxActive(Integer.valueOf(bundle
					.getString("redis.pool.maxActive")));
			config.setMaxIdle(Integer.valueOf(bundle
					.getString("redis.pool.maxIdle")));
			config.setMaxWait(Long.valueOf(bundle.getString("redis.pool.maxWait")));
			config.setTestOnBorrow(Boolean.valueOf(bundle
					.getString("redis.pool.testOnBorrow")));
			config.setTestOnReturn(Boolean.valueOf(bundle
					.getString("redis.pool.testOnReturn")));
			pool = new JedisPool(config, bundle.getString("redis.ip"),
					Integer.valueOf(bundle.getString("redis.port")));
		}
		return pool;
	}
	
	public static JedisPool getConnection() {
		// ②如果poolThreadLocal没有本线程对应的JedisPool创建一个新的JedisPool,将其保存到线程本地变量中。
		if (poolThreadLocal.get() == null) {
			pool = RedisAPI.getPool();
			poolThreadLocal.set(pool);
			return pool;
		} else {
			return poolThreadLocal.get();// ③直接返回线程本地变量
		}
	}

	/**
	 * 返还到连接池
	 * 
	 * @param pool
	 * @param redis
	 */
	public static void returnResource(JedisPool pool, Jedis redis) {
		if (redis != null) {
			pool.returnResource(redis);
		}
	}

	/**
	 * 获取数据
	 * 
	 * @param key
	 * @return
	 */
	public static String get(String key) {
		String value = null;

		JedisPool pool = null;
		Jedis jedis = null;
		try {
			pool = getPool();
			jedis = pool.getResource();
			value = jedis.get(key);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 释放redis对象
			pool.returnBrokenResource(jedis);
			// 返还到连接池
			returnResource(pool, jedis);
		}

		return value;
	}

	/**
	 * 赋值数据
	 * 
	 * @param key
	 * @return
	 */
	public static String set(String key, String value) {
		String result = null;
		JedisPool pool = null;
		Jedis jedis = null;
		try {
			pool = getPool();
			jedis = pool.getResource();
			result = jedis.set(key, value);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 释放redis对象
			pool.returnBrokenResource(jedis);
			// 返还到连接池
			returnResource(pool, jedis);
		}

		return result;
	}

	/**
	 * 赋值数据
	 * 
	 * @param key
	 * @return
	 */
	public static Long sadd(String key, String value) {
		Long result = null;
		JedisPool pool = null;
		Jedis jedis = null;
		try {
			pool = getPool();
			jedis = pool.getResource();
			result = jedis.sadd(key, value);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 释放redis对象
			pool.returnBrokenResource(jedis);
			// 返还到连接池
			returnResource(pool, jedis);
		}

		return result;
	}
	
	/**
	 * 判断set中是否有值
	 * 
	 * @param key
	 * @return
	 */
	public static Boolean sismember(String key, String member) {
		Boolean result = null;
		JedisPool pool = null;
		Jedis jedis = null;
		try {
			pool = getPool();
			jedis = pool.getResource();
			result = jedis.sismember(key, member);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 释放redis对象
			pool.returnBrokenResource(jedis);
			// 返还到连接池
			returnResource(pool, jedis);
		}

		return result;
	}

}

redis.properties

#\u6700\u5927\u5206\u914d\u7684\u5bf9\u8c61\u6570
redis.pool.maxActive=1024
#\u6700\u5927\u80fd\u591f\u4fdd\u6301idel\u72b6\u6001\u7684\u5bf9\u8c61\u6570
redis.pool.maxIdle=200
#\u5f53\u6c60\u5185\u6ca1\u6709\u8fd4\u56de\u5bf9\u8c61\u65f6\uff0c\u6700\u5927\u7b49\u5f85\u65f6\u95f4
redis.pool.maxWait=1000
#\u5f53\u8c03\u7528borrow Object\u65b9\u6cd5\u65f6\uff0c\u662f\u5426\u8fdb\u884c\u6709\u6548\u6027\u68c0\u67e5
redis.pool.testOnBorrow=true
#\u5f53\u8c03\u7528return Object\u65b9\u6cd5\u65f6\uff0c\u662f\u5426\u8fdb\u884c\u6709\u6548\u6027\u68c0\u67e5
redis.pool.testOnReturn=true
#IP
redis.ip=127.0.0.1
#Port
redis.port=6379

bid.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
	pageEncoding="utf-8"%>




抢标秒杀



	

秒标





















list.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
	pageEncoding="utf-8"%>




抢标秒杀



	<%
		java.util.Map mapBean = (java.util.Map) request.getSession()
				.getAttribute("user");
	%>

	








中奖名单<% if (mapBean != null) { %> <%=mapBean.size()%>

<% } %>
<% if (mapBean != null) { for (String key : mapBean.keySet()) { %> <%=key%>--<%=mapBean.get(key)%>
<% } } %>

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

php网站源码教程大全
php网站源码教程大全

本专题整合了php网站源码相关教程,阅读专题下面的文章了解更多详细内容。

4

2025.12.31

视频文件格式
视频文件格式

本专题整合了视频文件格式相关内容,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

不受国内限制的浏览器大全
不受国内限制的浏览器大全

想找真正自由、无限制的上网体验?本合集精选2025年最开放、隐私强、访问无阻的浏览器App,涵盖Tor、Brave、Via、X浏览器、Mullvad等高自由度工具。支持自定义搜索引擎、广告拦截、隐身模式及全球网站无障碍访问,部分更具备防追踪、去谷歌化、双内核切换等高级功能。无论日常浏览、隐私保护还是突破地域限制,总有一款适合你!

7

2025.12.31

出现404解决方法大全
出现404解决方法大全

本专题整合了404错误解决方法大全,阅读专题下面的文章了解更多详细内容。

42

2025.12.31

html5怎么播放视频
html5怎么播放视频

想让网页流畅播放视频?本合集详解HTML5视频播放核心方法!涵盖<video>标签基础用法、多格式兼容(MP4/WebM/OGV)、自定义播放控件、响应式适配及常见浏览器兼容问题解决方案。无需插件,纯前端实现高清视频嵌入,助你快速打造现代化网页视频体验。

4

2025.12.31

关闭win10系统自动更新教程大全
关闭win10系统自动更新教程大全

本专题整合了关闭win10系统自动更新教程大全,阅读专题下面的文章了解更多详细内容。

3

2025.12.31

阻止电脑自动安装软件教程
阻止电脑自动安装软件教程

本专题整合了阻止电脑自动安装软件教程,阅读专题下面的文章了解更多详细教程。

3

2025.12.31

html5怎么使用
html5怎么使用

想快速上手HTML5开发?本合集为你整理最实用的HTML5使用指南!涵盖HTML5基础语法、主流框架(如Bootstrap、Vue、React)集成方法,以及无需安装、直接在线编辑运行的平台推荐(如CodePen、JSFiddle)。无论你是新手还是进阶开发者,都能轻松掌握HTML5网页制作、响应式布局与交互功能开发,零配置开启高效前端编程之旅!

2

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
进程与SOCKET
进程与SOCKET

共6课时 | 0.3万人学习

Redis+MySQL数据库面试教程
Redis+MySQL数据库面试教程

共72课时 | 6.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号