当前位置:首页 > Java技术 > 构建高性能服务(二)java高并发锁的3种实现

构建高性能服务(二)java高并发锁的3种实现

2022年09月17日 09:02:06Java技术8

构建高性能服务(二)java高并发锁的3种实现

来源http://www.xymyeah.com/?p=46

提高系统并发吞吐能力是构建高性能服务的重点和难点。通常review代码时看到synchronized是我都会想一想,这个地方可不可以优化。使用synchronized使得并发的线程变成顺序执行,对系统并发吞吐能力有极大影响,我的博文 http://maoyidao.iteye.com/blog/1149015 介绍了可以从理论上估算系统并发处理能力的方法。

 

那么对于必须使用synchronized的业务场景,这里提供几个小技巧,帮助大家减小锁粒度,提高系统并发能力。

 

初级技巧 - 乐观锁

乐观锁适合这样的场景:读不会冲突,写会冲突。同时读的频率远大于写。

 

以下面的代码为例,悲观锁的实现:

 

Java代码  
  1. public Object get(Object key) {  
  2.    synchronized(map) {  
  3.       if(map.get(key) == null) {  
  4.          // set some values  
  5.       }  
  6.   
  7.        return map.get(key);  
  8.    }  
  9. }  
 

 乐观锁的实现:

 

Java代码  
  1. public Object get(Object key) {  
  2.    Object val = null;  
  3.    if((val = map.get(key) == null) {  
  4.        // 当map取值为null时再加锁判断  
  5.        synchronized(map) {  
  6.            if(val = map.get(key) == null) {  
  7.                // set some value to map...  
  8.            }  
  9.         }  
  10.    }  
  11.   
  12.     return map.get(key);  
  13. }  

 

中级技巧 - String.intern()

乐观锁不能很好解决大量写冲突问题,但是如果很多场景下,锁实际上只是针对某个用户或者某个订单。比如一个用户必须先创建session,才能进行后面的操作。但是由于网络原因,创建用户session的请求和后续请求几乎同时达到,而并行线程可能会先处理后续请求。一般情况,需要对用户sessionMap加锁,比如上面的乐观锁。在这种场景下,可以讲锁限定到用户本身上,即从原来的

 

lock.lock();

    int num=storage.get(key);

    storage.set(key,num+1);

lock.unlock();

更改为:

lock.lock(key);

    int num=storage.get(key);

    storage.set(key,num+1);

lock.unlock(key);

这个比较类似于数据库表锁和行锁的概念,显然行锁的并发能力比表锁高很多。

 

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

 

Java代码   收藏代码
  1. public void doSomeThing(String uid) {  
  2.    synchronized(uid.intern()) {  
  3.        // ...  
  4.    }  
  5. }  

 

CopyOnWriteMap?

既然说到了“类似于数据库中的行锁的概念”,就不得不提一下MVCC,Java中CopyOnWrite类实现了MVCC。Copy On Write是这样一种机制。当我们读取共享数据的时候,直接读取,不需要同步。当我们修改数据的时候,我们就把当前数据Copy一份副本,然后在这个副本 上进行修改,完成之后,再用修改后的副本,替换掉原来的数据。这种方法就叫做Copy On Write。

但是,,,JDK并没有提供CopyOnWriteMap,为什么?下面有个很好的回答,那就是已经有了ConcurrentHashMap,为什么还需要CopyOnWriteMap?

Fredrik Bromee 写道
I guess this depends on your use case, but why would you need a CopyOnWriteMap when you already have a ConcurrentHashMap?

For a plain lookup table with many readers and only one or few updates it is a good fit.

Compared to a copy on write collection:

Read concurrency:

Equal to a copy on write collection. Several readers can retrieve elements from the map concurrently in a lock-free fashion.

Write concurrency:

Better concurrency than the copy on write collections that basically serialize updates (one update at a time). Using a concurrent hash map you have a good chance of doing several updates concurrently. If your hash keys are evenly distributed.

If you do want to have the effect of a copy on write map, you can always initialize a ConcurrentHashMap with a concurrency level of 1.
 

高级技巧 - 类ConcurrentHashMap

String.inter()的缺陷是类 String 维护一个字符串池是放在JVM perm区的,如果用户数特别多,导致放入字符串池的String不可控,有可能导致OOM错误或者过多的Full GC。怎么样能控制锁的个数,同时减小粒度锁呢?直接使用Java ConcurrentHashMap?或者你想加入自己更精细的控制?那么可以借鉴ConcurrentHashMap的方式,将需要加锁的对象分为多个bucket,每个bucket加一个锁,伪代码如下:

 

Java代码   收藏代码
  1. Map locks = new Map();  
  2. List lockKeys = new List();  
  3. for(int number : 1 - 10000) {  
  4.    Object lockKey = new Object();  
  5.    lockKeys.add(lockKey);  
  6.     locks.put(lockKey, new Object());  
  7. }  
  8.   
  9. public void doSomeThing(String uid) {  
  10.    Object lockKey = lockKeys.get(uid.hash() % lockKeys.size());  
  11.    Object lock = locks.get(lockKey);  
  12.      
  13.    synchronized(lock) {  
  14.       // do something  
  15.    }  
  16. }  
 

 

关于高性能缓存的设计,请参考构建高性能服务系列之一:http://www.xymyeah.com/?p=10


更多http://www.xymyeah.com/?p=46


作者:xymyeah
来源链接:https://blog.csdn.net/xymyeah/article/details/7700101

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/java/42434.html

分享给朋友:

“构建高性能服务(二)java高并发锁的3种实现” 的相关文章

java模拟多线程高并发

一、测试代码 import java.text.NumberFormat; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLa...

高并发秒杀系统API之SSM集成swagger与AdminLTE

高并发秒杀系统API之SSM集成swagger与AdminLTE

初衷与整理描述 Java高并发秒杀系统API是来源于网上教程的一个Java项目,慕课网地址:https://www.imooc.com/learn/587。 也是我接触Java的第一个项目。本来是一枚c#码农,公司计划部分业务转java,于是我利用业务时间自学Java才有了本文,...

java高并发处理

java高并发处理

      在java web项目开发者,最难解决的是高并发问题,我为搞并发解决方案,想出了一个解决方案。      a.应用层面:读写分离、缓存、队列、集群、令牌、系统拆分...

我就是这样应对Java面试中的多线程与高并发的,面试官都傻眼了

我就是这样应对Java面试中的多线程与高并发的,面试官都傻眼了

1. Java线程的创建方式 (1)继承thread类 thread类本质是实现了runnable接口的一个实例,代表线程的一个实例。启动线程的方式start方法。start是一个本地方法,执行后,执行run方法的代码。   (2)实现...

如何在高并发环境下设计出无锁的数据库操作(Java版本)

  一个在线2k的游戏,每秒钟并发都吓死人。传统的hibernate直接插库基本上是不可行的。我就一步步推导出一个无锁的数据库操作。   1. 并发中如何无锁。 一个很简单的思路,把并发转化成为单线程。Java的Disruptor就是一个很好...

Java内存模型JMM 高并发原子性可见性有序性简介 多线程中篇(十)

Java内存模型JMM 高并发原子性可见性有序性简介 多线程中篇(十)

不得不看的JMM介绍,JMM不同于java运行时内存区域划分,本文从区域划分拓展到JMM,多线程必备的知识点,并且从原子性可见性有序性三个角度深入的分析了JMM的在这几个点涉及的部分 JVM运行时内存结构回顾 在JVM相关的介绍中,有说到JAVA运行时的...

聊聊高并发(十四)理解Java中的管程,条件队列,Condition以及实现一个堵塞队列

这篇里面有一些主要的概念,理解概念是件有意义的事情,仅仅有理解概念才干在面对详细问题的时候找到正确的解决思路。先看一下管程的概念 第一次在书上看到管程这个中文名称认为非常迷糊,管程究竟是个什么东东,于是去找了英文原本对比一看。英文是Monitor,这不是监视器吗,更...

java高并发 pdf

推荐序一 推荐序二 推荐序三 推荐序四 前言 第一部分 多线程基础 第1章 快速认识线程 1.1 线程的介绍 1.2 快速创建并启动一个线程 1.3 线程的生命周期详解 1.4 线程的sta...

Java解决高并发下商品库存更新

Java解决高并发下商品库存更新

˃˃ _ JavaClub全栈架构师技术笔记"˃2019独角兽企业重金招聘Python工程师标准>>> 一、问题分析 先来就库存超卖的问题作描述:一般电子商务网站都会遇到如团购、秒杀、特价之类的活动,而这样的活动有一个...

互联网三高架构之高并发和高性能的理解

互联网三高架构之高并发和高性能的理解

互联网三高架构:高并发、高性能、高可用,简称三高(3H) 互联网应用系统开发肯定经常会看到高并发和高性能这两个词,可谓是耳熟能详,而具体的含义和关系真的如你所想的,真正的理解了吗? 先来看一个例子: 一个蓄水池,是1m*1m*1m=1立方米大小,有一个出水口,出...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。