需求:有一个标(理解成抢红包也行,accountBalance预赋值1000元),大家可以抢购,每个用户抢购成功后,更新最后标的总数,在并发情况下,使用redis的乐观锁,保证更新标总值正确性,先往redis放一个标的金额:
set accountBalance "1000"
实现方式如下:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>mybatisPage</groupId>
<artifactId>page</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging> <name>PageHelperSample</name>
<url>http://git.oschina.net/free/Mybatis-Sample</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> <dependencies>
<!-- jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- jstl -->
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency> <!-- log mybatis sql -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<!-- log mybatis sql --> <!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.4</version>
</dependency>
<!-- web -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency> <dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.4</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.5</version>
</dependency>
<!-- util -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<!-- redis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.1.0</version>
<type>jar</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>8.0.0.M3</version>
</plugin>
</plugins>
</build>
</project>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list> <servlet>
<servlet-name>bid</servlet-name>
<servlet-class>com.heli.mybatis.page.servlet.ReidsMatchServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>bid</servlet-name>
<url-pattern>/bid</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>list</servlet-name>
<servlet-class>com.heli.mybatis.page.servlet.ReidsMatchListServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>list</servlet-name>
<url-pattern>/list</url-pattern>
</servlet-mapping>
</web-app>
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<Object> 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<String, String> map = new HashMap<String, String>();
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("中标名单-------------------------");
} }
工具类
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<JedisPool> poolThreadLocal = new ThreadLocal<JedisPool>(); /**
* 构建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"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>抢标秒杀</title>
<style type="text/css">
* {
margin: 0;
} html, body {
height: 100%;
} .wrapper {
min-height: 100%;
height: auto !important;
height: 100%;
margin: 0 auto -155px;
} .footer, .push {
height: 155px;
} .middle {
text-align: center;
margin: 0 auto;
width: 600px;
height: auto;
}
</style>
</head>
<body>
<form name="formBid" action="bid" method="get">
<div class="wrapper">
<div class="middle">
<h1 style="padding: 0px 0 10px;">秒标</h1>
<br> <br> <br> <br> <br> <br> <br><br> <br> <br> <input type="submit"style="width: 600px; height: 200px" value="秒杀" /> <br> <br><br> <br> <br></div></div></form><form name="formList" action="list" method="post"><div class="wrapper"><div class="middle"><br> <br> <br> <br> <br> <inputtype="submit" style="width: 200px; height: 50px" value="查看秒杀结果" /></div></div></form></body></html>
list.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>抢标秒杀</title>
<style type="text/css">
* {
margin: 0;
} html, body {
height: 100%;
} .wrapper {
min-height: 100%;
height: auto !important;
height: 100%;
margin: 0 auto -155px;
} .footer, .push {
height: 155px;
} .middle {
text-align: center;
margin: 0 auto;
width: 600px;
height: auto;
}
</style>
</head>
<body>
<%
java.util.Map<String, String> mapBean = (java.util.Map<String, String>) request.getSession()
.getAttribute("user");
%> <form name="formList" action="list" method="post">
<div class="wrapper">
<div class="middle">
<br> <br> <br> <br> <br> <br> <br>
<br>
<h1 style="padding: 0px 0 10px;">
中奖名单<%if (mapBean != null) {
%><%=mapBean.size()%></h1><%
}
%><br><%if (mapBean != null) {
for (String key : mapBean.keySet()) {
%><%=key%>--<%=mapBean.get(key)%><br><%
}
}
%></div></div></form></body></html>