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