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

作者:程序员小董 时间:2023-07-09 08:45:49 

假定存在这样一种情况

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


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,这个方法就不多说了。

来源:http://blog.csdn.net/dlf123321/article/details/57955491

标签:java,高并发,数据库
0
投稿

猜你喜欢

  • android异步消息机制 从源码层面解析(2)

    2023-08-06 15:55:39
  • 浅谈Java三大框架与应用

    2023-04-16 18:25:01
  • Java的“Goto”与标签及使用详解

    2023-11-11 03:56:09
  • java入门概念个人理解之package与import浅析

    2021-07-09 10:18:55
  • 解决Mybatis中foreach嵌套使用if标签对象取值的问题

    2023-11-23 06:02:02
  • Java客户端调用.NET的WebService实例

    2023-11-03 17:22:00
  • springMVC自定义注解,用AOP来实现日志记录的方法

    2023-11-29 13:58:53
  • 移动开发Spring Boot外置tomcat教程及解决方法

    2023-08-25 11:47:33
  • Java设计模式之工厂模式实现方法详解

    2023-11-26 07:55:51
  • SpringBoot中@ConfigurationProperties注解实现配置绑定的三种方法

    2023-03-19 12:36:25
  • 一篇文章带你入门Java基本概念

    2023-11-26 01:17:08
  • Java编程用两个栈实现队列代码分享

    2023-03-22 01:05:05
  • java 进制转换实例详解

    2023-07-05 11:53:45
  • Java实现简单的万年历

    2023-11-29 08:31:24
  • java求100之内的素数(质数)简单示例

    2023-11-07 03:12:32
  • java导出生成word的简单方法

    2023-11-23 23:07:13
  • Java8新特性之泛型的目标类型推断_动力节点Java学院整理

    2023-11-26 10:38:21
  • java之assert关键字用法案例详解

    2022-07-10 23:01:32
  • SpringBoot持久化层操作支持技巧

    2023-11-24 06:40:32
  • Android TextView实现跑马灯效果的方法

    2023-07-30 20:44:12
  • asp之家 软件编程 m.aspxhome.com