欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java高并发下的唯一性验证

程序员文章站 2022-07-12 18:32:24
...

做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