并发的几个解决方案

1、 最简单的,页面的《提交》按钮,在点击后(校验通过后)Disabled,这样用户就不会重复点击《提交》按钮;

2、 数据库表增加唯一性索引(比如memberId),这个只能解决一般(为什么一般,继续看)并发插入的问题;

如果两条一样的数据插入,只有一条会成功,另外一次插入失败;

3、 使用乐观锁,在Table里增加一个version int字段,这个可以保证更新操作的并发问题;

Select version, .. from table1 where id=?

//do biz

Update set version=version+1 where id=? And version=?

在代码里,需要判断update返回的更新条数(count ==1?true:false)来判断本次更新是否成功

4、 使用悲观锁,一般使用数据库提供的功能(不建议);

select for update(悲观锁)

//do biz

Update (事物提交,锁释放)

5、 有时候,数据库不能加唯一性索引(比如由memberId,status两个字段,业务要求memberId,status=approved的数据唯一,其他状态的数据不做限制);

这个时候,需要寻求新的协调中心(之前是数据库光荣的承担这一角色),我们现在使用的Memcached可以完成这个任务,利用memcached协议规定add的原子性,详细请点击这里

我们在信联项目里使用了这个方案,有一个缺点,就是需要侵入到业务代码里来控制并发,参考代码:     

方式1-回调(推荐):

 try {

     concurrentTemplate.execute(key, new ConcurrentCallback() {

         public Object doInConcurrent() {

             // do your biz

             return null;

         }

     });

 } catch (ConcurrentException e) {

     // catch this exception

     // return value;

 }

方式二:自助获取和释放锁

 ConcurrentLock lock = null;

 try {

     lock = concurrentTemplate.acquireLock(key);

     // do your biz

 } catch (ConcurrentException e) {

     // catch this exception

     // return value;

 } finally {

     concurrentTemplate.releaseLock(lock);

 }

ConcurrentTemplate实现代码,很简单

public class ConcurrentTemplate implements InitializingBean {

    private static final Logger  log                = LoggerFactory.getLogger(ConcurrentTemplate.class);
    private static final Boolean DEFAULT_VALUE      = Boolean.TRUE;
    
    private CacheTemplate cacheTemplate;

   
    public void afterPropertiesSet() throws Exception {
        if (cacheTemplate== null) throw new IllegalArgumentException("cacheTemplateis null.");
    }

    /**
     * 执行具体的并发控制,需要实现ConcurrentCallback接口
     * 
     * @param key
     * @param ccb
     * @return Object
     * @throws ConcurrentException
     */
    public Object execute(String key, ConcurrentCallback ccb) throws ConcurrentException {
        ConcurrentLock lock = null;
        try {
            // 默认值为true,防止Counter出错引起业务不可继续执行
            lock = acquireLock(key);
            return ccb.doInConcurrent();
        } finally {
            releaseLock(lock);
        }
    }

    /**
     * 获取一把锁,需要捕获异常
     * 
     * @param key 锁的粒度,由业务来定义
     * @throws ConcurrentException 发生并发,抛异常
     * @see releaseLock
     */
    public ConcurrentLock acquireLock(String key) throws ConcurrentException {
        // 默认值为true,防止Counter出错引起业务不可继续执行
        boolean flag = true;
        ConcurrentLock lock = null;
        try {
            flag = cacheTemplate.add(key, DEFAULT_VALUE);
        } catch (StoreException e) {
            log.error("key:" + key, e);
        }
        if (!flag) throw new ConcurrentException();

        lock = new ConcurrentLock();
        lock.setKey(key);
        return lock;
    }

    /**
     * 释放锁,一般要放在finally里操作
     * 
     * @param key 锁的粒度
     */
    public void releaseLock(ConcurrentLock lock) {
        if (lock == null) return;
        releaseLock(lock.getKey());
    }

    /**
     * 释放锁,一般要放在finally里操作
     * 
     * @param key 锁的粒度
     */
    private void releaseLock(String key) {
        try {
            cacheTemplate.remove(key);
        } catch (StoreException e) {
            log.error("key:" + key, e);        }
    }

}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值