java高并发写入用户信息到数据库的几种方法

 更新时间:2017年03月02日 16:24:28   作者:程序员小董  
本文主要介绍了java高并发写入用户信息到数据库的几种方法,具有很好的参考价值。下面跟着小编一起来看下吧

假定存在这样一种情况

多个用户对数据库进行写,我们的业务逻辑规定,每个用户只能写一次,大部分用户也只发一次请求。

public void write(Uers u){ 
 // do something 
} 

但是有一种情况(1%的情况下吧)的就是有的用户会发两次甚至更多次写请求(因为数据库限制,我们不方便在主键上做文章)。

如果这个特殊的用户发送的两次请求时间间隔比较大,那就简单了,再每次写入的时候,写去数据库里看看,这个人有没有写过,如果已经写过了,就直接抛弃这个请求。

public void write(Uers u){ 
 if(!checkIfExistUser(u)){ 
   // do something 
  } 
} 

不过最大的问题就是,如果用户几乎在瞬时,发送了两个写操作。

而且假定我们的do something比较耗时,那么上面的策略就有可能失败。

为啥失败?我不用解释了吧。

那咋办?

方法一

万年不变的synchronized。

public synchronized void write(Uers u){ 
 if(!checkIfExistUser(u)){ 
   // do something 
  } 
} 

当然,我们得承认,有了上面的方法,就不会出现,数据库里有两条张三的记录了

但上面的锁的粒度太大了,张三写的时候,李四也不能写了。

其实我们想要的只是:张三自己本人,不能同时多次写入。

方法二

类 String 维护一个字符串池。 当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串。可见,当String相同时,String.intern()总是返回同一个对象,因此就实现了对同一用户加锁。由于锁的粒度局限于具体用户,使系统获得了最大程度的并发。

public synchronized void write(Uers u){ 
  synchronized(u.getUserId.intern()) { 
   // do something 
  } 
}

上面的思路就保证了张三写的时候,李四可以写,但是不能两个张三一块写。

方法三

其实我个人觉得,方法二已经很好了,如果非要说方法二还有什么问题的话,只能说:

String.inter()的缺陷是类 String 维护一个字符串池是放在JVM perm区的,如果用户数特别多,导致放入字符串池的String不可控,有可能导致OOM错误或者过多的Full GC。

那咋办?

public synchronized void write(Uers u){ 
  String userSuffix=getSuffix(u); 
  synchronized(userSuffix.intern()) { 
   // do something 
  } 
} 

至于那个获得后缀的策略,大家自己想。

有了这个策略,我就能保证1亿个用户,可能只有10000个不同的后缀。

有可能张三李四的后缀一样,但是张三李四同时发请求的概率,应该也不会太大。就算真的同时发了,那你等一下不行么?

方法四

Map locks = new Map();   
List lockKeys = new List();   
for(int number : 1 - 10000) {   
  Object lockKey = new Object();   
  lockKeys.add(lockKey);   
  locks.put(lockKey, new Object());   
}    
public void doSomeThing(String uid) {   
  Object lockKey = lockKeys.get(uid.hash() % lockKeys.size());   
  Object lock = locks.get(lockKey);   
  synchronized(lock) {   
   // do something   
  }   
} 
 

个人感觉和方法三的核心差不多。

方法五

如果是集群情况下,两个张三几乎瞬时进入两台服务器,那java语言级别的锁都得报废。

可以使用redis的分布式锁

方法六

使用zookeeper

只是听说有这么一个思路,但是本人没用过zookeeper,这个方法就不多说了。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持脚本之家!

相关文章

  • JavaWeb开发之Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架

    JavaWeb开发之Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JC

    这篇文章主要介绍了JavaWeb开发之Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架的相关资料,需要的朋友可以参考下
    2016-12-12
  • 搜索一文入门ElasticSearch(节点 分片 CRUD 倒排索引 分词)

    搜索一文入门ElasticSearch(节点 分片 CRUD 倒排索引 分词)

    这篇文章主要为大家介绍了搜索一文入门ElasticSearch(节点 分片 CRUD 倒排索引 分词)的基础详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • @PathVariable、@RequestParam和@RequestBody的区别

    @PathVariable、@RequestParam和@RequestBody的区别

    本文主要介绍了@PathVariable、@RequestParam和@RequestBody的区别和使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • Spring整合消息队列RabbitMQ流程

    Spring整合消息队列RabbitMQ流程

    Spring整合RabbitMQ很容易,但是整合的目的是为了使用,那要使用RabbitMQ就要对其有一定的了解,不然容易整成一团浆糊。因为说到底,Spring只是在封装RabbitMQ的API,让其更容易使用而已,废话不多说,让我们一起整它
    2023-03-03
  • 解决Feign切换client到okhttp无法生效的坑(出现原因说明)

    解决Feign切换client到okhttp无法生效的坑(出现原因说明)

    这篇文章主要介绍了解决Feign切换client到okhttp无法生效的坑(出现原因说明),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • Spring Security角色继承实现过程解析

    Spring Security角色继承实现过程解析

    这篇文章主要介绍了Spring Security角色继承实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • Java实用工具之使用oshi获取主机信息的方法

    Java实用工具之使用oshi获取主机信息的方法

    这篇文章主要介绍了Java实用工具之使用oshi获取主机信息的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • Spring @Cacheable读取配置常量方式

    Spring @Cacheable读取配置常量方式

    这篇文章主要介绍了Spring @Cacheable读取配置常量方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • maven 删除下载失败的包的方法

    maven 删除下载失败的包的方法

    本文介绍了当Maven包报红时,使用删除相关文件的方法来解决该问题,具有一定的参考价值,感兴趣的可以了解一下
    2023-09-09
  • 封装jndi操作ldap服务器的工具类

    封装jndi操作ldap服务器的工具类

    这篇文章主要介绍了封装JNDI操作LDAP服务器的工具类,使用者只需要会使用List,Map 数据结构,大家参考使用吧
    2014-01-01

最新评论