java细粒度锁_Java细粒度锁实现的3种方式

68bfdf3476ef0c6acd06a67b23f67ce1.gif

最近在工作上碰见了一些高并发的场景需要加锁来保证业务逻辑的正确性,并且要求加锁后性能不能受到太大的影响。初步的想法是通过数据的时间戳,id等关键字来加锁,从而保证不同类型数据处理的并发性。而java自身api提供的锁粒度太大,很难同时满足这些需求,于是自己动手写了几个简单的扩展…

68bfdf3476ef0c6acd06a67b23f67ce1.gif

1. 分段锁

借鉴concurrentHashMap的分段思想,先生成一定数量的锁,具体使用的时候再根据key来返回对应的lock。这是几个实现里最简单,性能最高,也是最终被采用的锁策略,代码如下:

/**

* 分段锁,系统提供一定数量的原始锁,根据传入对象的哈希值获取对应的锁并加锁

* 注意:要锁的对象的哈希值如果发生改变,有可能导致锁无法成功释放!!!

*/

public class SegmentLock{

private Integer segments = 16;//默认分段数量

private final HashMap lockMap = new HashMap<>();

public SegmentLock(){

init(null, false);

}

public SegmentLock(Integer counts, boolean fair){

init(counts, fair);

}

private void init(Integer counts, boolean fair){

if (counts != null) {

segments = counts;

}

for (int i = 0; i < segments; i++) {

lockMap.put(i, new ReentrantLock(fair));

}

}

public void lock(T key){

ReentrantLock lock = lockMap.get(key.hashCode() % segments);

lock.lock();

}

public void unlock(T key){

ReentrantLock lock = lockMap.get(key.hashCode() % segments);

lock.unlock();

}

}

2. 哈希锁

上述分段锁的基础上发展起来的第二种锁策略,目的是实现真正意义上的细粒度锁。每个哈希值不同的对象都能获得自己独立的锁。在测试中,在被锁住的代码执行速度飞快的情况下,效率比分段锁慢 30% 左右。如果有长耗时操作,感觉表现应该会更好。代码如下:

public class HashLock{

private boolean isFair = false;

private final SegmentLock segmentLock = new SegmentLock<>();//分段锁

private final ConcurrentHashMap lockMap = new ConcurrentHashMap<>();

public HashLock(){

}

public HashLock(boolean fair){

isFair = fair;

}

public void lock(T key){

LockInfo lockInfo;

segmentLock.lock(key);

try {

lockInfo = lockMap.get(key);

if (lockInfo == null) {

lockInfo = new LockInfo(isFair);

lockMap.put(key, lockInfo);

} else {

lockInfo.count.incrementAndGet();

}

} finally {

segmentLock.unlock(key);

}

lockInfo.lock.lock();

}

public void unlock(T key){

LockInfo lockInfo = lockMap.get(key);

if (lockInfo.count.get() == 1) {

segmentLock.lock(key);

try {

if (lockInfo.count.get() == 1) {

lockMap.remove(key);

}

} finally {

segmentLock.unlock(key);

}

}

lockInfo.count.decrementAndGet();

lockInfo.unlock();

}

private static class LockInfo{

public ReentrantLock lock;

public AtomicInteger count = new AtomicInteger(1);

private LockInfo(boolean fair){

this.lock = new ReentrantLock(fair);

}

public void lock(){

this.lock.lock();

}

public void unlock(){

this.lock.unlock();

}

}

}

3. 弱引用锁

哈希锁因为引入的分段锁来保证锁创建和销毁的同步,总感觉有点瑕疵,所以写了第三个锁来寻求更好的性能和更细粒度的锁。这个锁的思想是借助java的弱引用来创建锁,把锁的销毁交给jvm的垃圾回收,来避免额外的消耗。

有点遗憾的是因为使用了ConcurrentHashMap作为锁的容器,所以没能真正意义上的摆脱分段锁。这个锁的性能比 HashLock 快10% 左右。锁代码:

/**

* 弱引用锁,为每个独立的哈希值提供独立的锁功能

*/

public class WeakHashLock{

private ConcurrentHashMap> lockMap = new ConcurrentHashMap<>();

private ReferenceQueue queue = new ReferenceQueue<>();

public ReentrantLock get(T key){

if (lockMap.size() > 1000) {

clearEmptyRef();

}

WeakReference lockRef = lockMap.get(key);

ReentrantLock lock = (lockRef == null ? null : lockRef.get());

while (lock == null) {

lockMap.putIfAbsent(key, new WeakLockRef<>(new ReentrantLock(), queue, key));

lockRef = lockMap.get(key);

lock = (lockRef == null ? null : lockRef.get());

if (lock != null) {

return lock;

}

clearEmptyRef();

}

return lock;

}

@SuppressWarnings("unchecked")

private void clearEmptyRef(){

Reference extends ReentrantLock> ref;

while ((ref = queue.poll()) != null) {

WeakLockRef weakLockRef = (WeakLockRef) ref;

lockMap.remove(weakLockRef.key);

}

}

private static final class WeakLockRef extends WeakReference{

final T key;

private WeakLockRef(K referent, ReferenceQueue super K> q, T key){

super(referent, q);

this.key = key;

}

}

}

后记

最开始想借助 locksupport 和 AQS 来实现细粒度锁,写着写着发现正在实现的东西和java 原生的锁区别不大,于是放弃改为对java自带锁的封装,浪费了不少时间。

实际上在实现了这些细粒度锁之后,又有了新的想法,比如可以通过分段思想将数据提交给专门的线程来处理,可以减少大量线程的阻塞时间,留待日后探索…

- THE END -

作者简介

68bfdf3476ef0c6acd06a67b23f67ce1.gif

Mr.W

白天搬砖,晚上砌梦想。

相信每个人有故事,程序员更是有许多事故,书写最接地气的程序员故事,为大家找出更好的资料。

原文始发于微信公众号(JAVA的学习之路):Java细粒度锁实现的3种方式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/441932.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【POJ - 1062】【nyoj - 510】昂贵的聘礼 (Dijkstra最短路+思维)

题干&#xff1a; 年轻的探险家来到了一个印第安部落里。在那里他和酋长的女儿相爱了&#xff0c;于是便向酋长去求亲。酋长要他用10000个金币作为聘礼才答应把女儿嫁给他。探险家拿不出这么多金币&#xff0c;便请求酋长降低要求。酋长说&#xff1a;"嗯&#xff0c;如果…

php e notice,PHP函数之error_reporting(E_ALL ^ E_NOTICE)详细说明

举例说明&#xff1a;在Windows环境下&#xff1a;原本在php4.3.0中运行正常的程序&#xff0c;在4.3.1中为何多处报错&#xff0c;大体提示为&#xff1a;Notice:Undefined varialbe:变量名称. 例如有如下的代码&#xff1a; 复制代码 代码如下:if (!$tmp_i) { $tmp_i10; }在4…

【POJ - 3169】 Layout(差分约束+spfa)(当板子记?)

题干&#xff1a; Like everyone else, cows like to stand close to their friends when queuing for feed. FJ has N (2 < N < 1,000) cows numbered 1..N standing along a straight line waiting for feed. The cows are standing in the same order as they are nu…

php中pregmatch,php中preg_match的isU代表什么意思

isU是大小写分的意思&#xff0c;这里s还有则不包括换行符而U是反转了匹配数量的值使其不是默认的重复,大概就是这样了个体我们看文章。正则后面的/(.*)/isU &#xff0c;“isU”参数代表什么意思&#xff1f;这是正则中的修正符.i是同时查找大小写字母,s是圆点(.)匹配所有字符…

【POJ - 1511】 Invitation Cards(Dijkstra + 反向建图 多源到单源最短路的处理)

题干&#xff1a; In the age of television, not many people attend theater performances. Antique Comedians of Malidinesia are aware of this fact. They want to propagate theater and, most of all, Antique Comedies. They have printed invitation cards with all…

【HDU - 3499】 Flight (单源最短路+优惠问题)

题干&#xff1a; Recently, Shua Shua had a big quarrel with his GF. He is so upset that he decides to take a trip to some other city to avoid meeting her. He will travel only by air and he can go to any city if there exists a flight and it can help him re…

php能不能用MyBatis,Mybatis与Ibatis的区别

Mybatis与Ibatis的区别:1、Mybatis实现了接口绑定&#xff0c;使用更加方便在ibatis2.x中我们需要在DAO的实现类中指定具体对应哪个xml映射文件&#xff0c;而Mybatis实现了DAO接口与xml映射文件的绑定&#xff0c;自动为我们生成接口的具体实现&#xff0c;使用起来变得更加省…

【51Nod - 1001 】 数组中和等于K的数对 (排序+ 尺取)

题干&#xff1a; 给出一个整数K和一个无序数组A&#xff0c;A的元素为N个互不相同的整数&#xff0c;找出数组A中所有和等于K的数对。例如K 8&#xff0c;数组A&#xff1a;{-1,6,5,3,4,2,9,0,8}&#xff0c;所有和等于8的数对包括(-1,9)&#xff0c;(0,8)&#xff0c;(2,6)…

linux php oauth安装,Linux php 扩展安装 mongo ,redis ,soap,imap,pdo_mysql,oauth

安装mongodb 参看文章&#xff1a;2.安装redisyum install gitgit clone git://github.com/owlient/phprediscd phpredis/usr/local/php/bin/phpize./configure --with-php-config/usr/local/php/bin/php-configmake && make install如果上述出现报错&#xff0c;可以尝…

*【CF#510C】Fox And Names (拓扑排序)

题干&#xff1a; Fox Ciel is going to publish a paper on FOCS (Foxes Operated Computer Systems, pronounce: "Fox"). She heard a rumor: the authors list on the paper is always sorted in the lexicographical order. After checking some examples, she…

java质,JAVA分解质因子 - osc_r1gtal48的个人空间 - OSCHINA - 中文开源技术交流社区

/*题目分解质因数(5分)题目内容&#xff1a;每个非素数(合数)都可以写成几个素数(也可称为质数)相乘的形式&#xff0c;这几个素数就都叫做这个合数的质因数。比如&#xff0c;6可以被分解为2x3&#xff0c;而24可以被分解为2x2x2x3。现在&#xff0c;你的程序要读入一个[2,100…

【HDU - 5605】 geometry(水,数学题,推公式)

题干&#xff1a; There is a point PP at coordinate (x,y)(x,y). A line goes through the point, and intersects with the postive part of X,YX,Yaxes at point A,BA,B. Please calculate the minimum possible value of |PA|∗|PB||PA|∗|PB|. Input the first line…

matlab如何画函数的外包络曲线,怎样在MATLAB中划出一个函数的包络线?

沧海一幻觉下面是一系列关于MATLAB的包络线的程序&#xff1a;%这是定义了一个函数&#xff1a;function [up,down] envelope(x,y,interpMethod)%ENVELOPE gets the data of upper and down envelope of the known input (x,y).%% Input parameters:% x the abscissa of the g…

【51nod - 1087】 1 10 100 1000(找规律推公式,水,map)

题干&#xff1a; 1,10,100,1000...组成序列1101001000...&#xff0c;求这个序列的第N位是0还是1。 Input 第1行&#xff1a;一个数T&#xff0c;表示后面用作输入测试的数的数量。&#xff08;1 < T < 10000) 第2 - T 1行&#xff1a;每行1个数N。&#xff08;1 &…

php 异常错误信息用处,关于PHP中异常错误的处理详细介绍

1. 错误报告级别 error_reporting()error_reporting(int $level);PHP 5.4 及以上 E_ALL 包含了 E_STRICT。PHP Manual 所有的错误级别。范例&#xff1a;<?php // 关闭所有PHP错误报告error_reporting(0);// Report simple running errorserror_reporting(E_ERROR | E_WARN…

matlab计算流函数,hanyeah

上面的网址不知道什么时候就打不开了&#xff0c;赶紧保存一份&#xff0c;要不想看都看不到了。什么是流函数&#xff0c;什么是位函数(势函数)&#xff0c;可以自己搜索。说说我这里的应用场景。空间放一些电荷&#xff0c;我们能够算出任意一点的电场强度——一个矢量&#…

【51Nod - 1279】 扔盘子(思维)(on-p会超时)

题干&#xff1a; 有一口井&#xff0c;井的高度为N&#xff0c;每隔1个单位它的宽度有变化。现在从井口往下面扔圆盘&#xff0c;如果圆盘的宽度大于井在某个高度的宽度&#xff0c;则圆盘被卡住&#xff08;恰好等于的话会下去&#xff09;。 盘子有几种命运&#xff1a;1、…

java 内部类私有成员 能访问,为什么外部Java类可以访问内部类私有成员?

HUX布斯如果您想隐藏内部类的私有成员&#xff0c;您可以与公共成员定义一个接口&#xff0c;并创建一个实现此接口的匿名内部类。下面的例子&#xff1a;class ABC{private interface MyInterface{void printInt();}private static MyInterface mMember new MyInterface(){pr…

【HDU - 6290】 奢侈的旅行 (对题目预处理 + DIjkstra最短路)

题干&#xff1a; 高玩小Q不仅喜欢玩寻宝游戏&#xff0c;还喜欢一款升级养成类游戏。在这个游戏的世界地图中一共有nn个城镇&#xff0c;编号依次为11到nn。 这些城镇之间有mm条单向道路&#xff0c;第ii 条单项道路包含四个参数ui,vi,ai,biui,vi,ai,bi&#xff0c;表示一条…

数学建模matlab推荐,推荐数学建模matlab方法整理 - 图文

disp()函数的常见用法1、显示字符串 >> disp(sqrt(2)) sqrt(2)将要显示的字符串必须放在单引号里面&#xff01;&#xff01;&#xff01; 2、显示结果 >> disp(sqrt(2)) 1.41423、显示多个字符>> disp([sqrt(2),num2str(sqrt(2))]) sqrt(2)1.4142格式必须如…