`

java高并发下的唯一性验证

阅读更多

做java ee程序基本上都会遇到唯一性的问题,我们通常不考虑并发性的问题的情况下的做法是:先根据条件去数据中查询是否存在,如果存在则提示不唯一,否则插入

 

下面是一个简单的例子, 向表t_test_curr插入数据,t_test_curr表包含两个字段,一个id(主键,自增长),一个username,要求唯一

1 不考虑并发性的做法:

  

    public void testConcurr (String username) {
        //t_test_curr并发测试表名
        String uniqueSql = new StringBuilder("select 1 from t_test_curr where username = ?").toString();
        List<SerializableJSONObject> uniqueList = this.baseDao.sqlQueryResult4Cache(uniqueSql, new Object[]{username});
        if (uniqueList != null && uniqueList.size() > 0) {
            throw new OperateFailureException("用户名重复!");
        }
        String saveSql = new StringBuilder("insert into t_test_curr(username) values (?)").toString();
        this.baseDao.executeDdlSql(saveSql, new Object[]{username});
    }

 测试代码:

 

class TestConThread implements Runnable {

    private ActivityService activityService;

    TestConThread (ActivityService activityService) {
        this.activityService = activityService;
    }

    @Override
    public void run () {
        activityService.testConcurr("malone");
    }
}

    public static void main (String[] args) throws Exception{
        ApplicationContext ctx = BaseTest.getCtx();
        ActivityService activityService = (ActivityService)ctx.getBean("activityService");
        TestConThread testConThread = new TestConThread(activityService);
        Thread[] threads = new Thread[10];
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(testConThread);
            threads[i].start();
        }
        for (int i = 0; i < 10; i++) {
            threads[i].join();
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

    用上面的代码测试,启动十个线程,数据库中插入了9条记录,所以这种代码在并发的情况下没有任何抵抗能力,代码中打印出来的结果为:2582

 

2 使用锁,锁上当前方法:

  

    public void testConcurr2 (String username) {
        lock.lock();
        try {
            String uniqueSql = new StringBuilder("select 1 from t_test_curr where username = ?").toString();
            List<SerializableJSONObject> uniqueList = this.baseDao.sqlQueryResult4Cache(uniqueSql, new Object[]{username});
            if (uniqueList != null && uniqueList.size() > 0) {
                throw new OperateFailureException("用户名重复!");
            }
            String saveSql = new StringBuilder("insert into t_test_curr(username) values (?)").toString();
            this.baseDao.executeDdlSql(saveSql, new Object[]{username});
        } finally {
            lock.unlock();
        }
    }

   上面的线程类的run方法调用当前方法,这样数据不会重复,只会有一条,但是用锁会对吞吐量造成一定的影响,代码打印结果:3683,从打印结果来看,基本上慢了1/3

 

3 使用并发集合:

  

    public void testConcurr1 (String username) {
        if (isInCache(username)) {
            throw new OperateFailureException("你输入的用户名已经存在!");
        }
        String uniqueSql = new StringBuilder("select 1 from t_test_curr where username = ?").toString();
        List<SerializableJSONObject> uniqueList = this.baseDao.sqlQueryResult4Cache(uniqueSql, new Object[]{username});
        if (uniqueList != null && uniqueList.size() > 0) {
            throw new OperateFailureException("用户名重复!");
        }
        String saveSql = new StringBuilder("insert into t_test_curr(username) values (?)").toString();
        this.baseDao.executeDdlSql(saveSql, new Object[]{username});
        removeFromCache(username);
    }

    public boolean isInCache (String username) {
        if (map.containsKey(username)) {
            return true;
        } else {
            map.put(username, username);
            return false;
        }
    }

    public void removeFromCache (String username) {
        map.remove(username);
    }

     上面的代码逻辑为:在service中放一个ConcurrentHashMap,当请求来时,就把username和map里的数据比较,如果重复,就直接提示重复,如果不重复就放入的集合中,然后在验证在数据库中是否重复,如果重复则提示,不重复则插入,并移除并发集合中的username;这样做的好处时,不会锁住代码,系统吞吐量不会受影响,而且在并发环境下不会出现重复数据,测试代码打印结果:2459

分享到:
评论
2 楼 abc08010051 2018-02-07  
张延龙地盘 写道
多实例下就不行了吧

是的,多实例直接上分布式锁
1 楼 张延龙地盘 2018-01-02  
多实例下就不行了吧

相关推荐

Global site tag (gtag.js) - Google Analytics