因为高并发的时候是有很多用户在访问,导致出现系统数据不正确、丢失数据现象,所以想到的是用队列解决,其实队列解决的方式也可以处理,比如我们在竞拍商品、转发评论微博或者是秒杀商品等,同一时间访问量特别大,队列在此起到特别的作用,将所有请求放入队列,以毫秒计时单位,有序的进行,从而不会出现数据丢失系统数据不正确的情况。
加并发锁的原理:
就是给一些大批量业务对象在同时并发访问时,通过对象中的某些唯一标识字段来作为对象访问(操作)服务器的标识,一旦能够有一个对象访问服务之后,就锁住该对象,直到该对象执行完了之后,才释放锁资源;允许接下来的对象进行访问对象操作。
1.创建一个队列锁的interface(例如IBusinessLockService),
interface中的方法有lock(加锁),unlock(解锁)方法 以及该队列锁接口的实现类;
加并发锁的原理:
就是给一些大批量业务对象在同时并发访问时,通过对象中的某些唯一标识字段来作为对象访问(操作)服务器的标识,一旦能够有一个对象访问服务之后,就锁住该对象,直到该对象执行完了之后,才释放锁资源;允许接下来的对象进行访问对象操作。
1.创建一个队列锁的interface(例如IBusinessLockService),
interface中的方法有lock(加锁),unlock(解锁)方法 以及该队列锁接口的实现类;
加锁的原理是:使用redis(jedis)中的setnx 命令用于加锁;
如果 SETNX 返回 1 ,说明客户端已经获得了锁, key 设置的unix时间则指定了锁失效的时间。之后客户端可以通过 DEL lock.foo 来释放锁。
如果 SETNX 返回 0 ,说明 key 已经被其他客户端上锁了。如果锁是非阻塞(non blocking lock)的,我们可以选择返回调用,或者进入一个重试循环,直到成功获得锁或重试超时(timeout)。
接口实现类代码:
/**
* 锁定某个业务对象 timeout = 0 时,非阻塞调用,如果对象已锁定立刻返回失败 timeout > 0
* 时,阻塞调用,如果对象已锁定,会等待直到1)获取锁并返回成功 2)超时并返回失败
*
* @param mutex
* 互斥对象
* @param timeout
* 超时时间,单位:秒
* @return true:锁定成功,false:锁定失败
* long)
*/
@Override
public boolean lock(MutexElement mutex, int timeout) {
// 输入参数校验
if (mutex == null || mutex.getType() == null
|| StringUtils.isEmpty(mutex.getBusinessNo())) {
throw new RuntimeException("互斥参数为空");
}
Jedis jedis = null;
String key = mutex.getType().getPrefix() + mutex.getBusinessNo();
String value = mutex.getBusinessDesc();
try {
jedis = client.getResource();
long nano = System.nanoTime();
do {
LOGGER.debug("try lock key: " + key);
// 使用setnx模拟锁
Long i = jedis.setnx(key, value);
if (i == 1) { // setnx成功,获得锁
jedis.expire(key, mutex.getTtl());
LOGGER.debug("get lock, key: " + key + " , expire in " + mutex.getTtl() + " seconds.");
return true;
} else { // 存在锁
if (LOGGER.isDebugEnabled()) {
String desc = jedis.get(key);
LOGGER.debug("key: " + key + " locked by another business:" + desc);
}
}
if (timeout == 0) { // 非阻塞调用,则退出
break;
}
Thread.sleep(NumberConstants.NUMERAL_1000); // 每秒访问一次
} while ((System.nanoTime() - nano) < timeout * 1000l * 1000l * 1000l);
// 得不到锁,返回失败
return false;
} catch (JedisConnectionException je) {
LOGGER.error(je.getMessage(), je);
client.returnBrokenResource(jedis);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
} finally {
if (jedis != null) {
client.returnResource(jedis);
}
}
// 锁不再作为业务的的强制必要条件
// 发生REDIS异常,则不再处理锁
return true;
}
/**
* 解除某个业务对象锁定
*
* @author leo
* @param mutex
* 互斥对象
*/
@Override
public void unlock(MutexElement mutex) {
// 输入参数校验
if (mutex == null || mutex.getType() == null
|| StringUtils.isEmpty(mutex.getBusinessNo())) {
throw new RuntimeException("互斥参数为空");
}
Jedis jedis = null;
String key = mutex.getType().getPrefix() + mutex.getBusinessNo();
try {
jedis = client.getResource();
jedis.del(key);
LOGGER.debug("release lock, key :" + key);
} catch (JedisConnectionException je) {
LOGGER.error(je.getMessage(), je);
client.returnBrokenResource(jedis);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
} finally {
if (jedis != null) {
client.returnResource(jedis);
}
}
}
(2)实例模拟(登录界面的密码重置的锁控制):
<span style="white-space:pre"> </span>MutexElement mutex = new MutexElement(entity.getEmpCode(),
"UUMS_USER_CODE", MutexElementType.UUMS_USER_CODE);
boolean result = businessLockService.lock(mutex,
NumberConstants.ZERO);
//加锁成功
if (result) {
userService.updateUser(entity, empCode);
}
//解锁
businessLockService.unlock(mutex);
request.setAttribute("resetSuccuess", "重置密码成功!");