使用并发锁 控制并发数据的实例

因为高并发的时候是有很多用户在访问,导致出现系统数据不正确、丢失数据现象,所以想到的是用队列解决,其实队列解决的方式也可以处理,比如我们在竞拍商品、转发评论微博或者是秒杀商品等,同一时间访问量特别大,队列在此起到特别的作用,将所有请求放入队列,以毫秒计时单位,有序的进行,从而不会出现数据丢失系统数据不正确的情况。
加并发锁的原理: 
就是给一些大批量业务对象在同时并发访问时,通过对象中的某些唯一标识字段来作为对象访问(操作)服务器的标识,一旦能够有一个对象访问服务之后,就锁住该对象,直到该对象执行完了之后,才释放锁资源;允许接下来的对象进行访问对象操作。
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", "重置密码成功!");






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值