-
Java高并发12-避免伪共享和锁机制
一、复习
二、如何避免伪共享
-
在JDK8之前是使用填充字节的方式来避免伪共享的,我们最终的目的其实就是希望单个变量能够独占一个缓存行。 -
我们举一个类的例子
package com.ruigege.OtherFoundationOfConcurrent2;
public class FiledLong {
public volatile long value =0L;
public long p1,p2,p3,p4,p5,p6;
}
-
可以看这个类,如果cache行伪64个字节,那么正好能够占满,七个long类型的变量,其中p1-p6都是用来占位,还有一个对象的头占用八个字节,正好64个字节。 -
在JDK8中提供了一个注解,用于避免伪共享
@sun.misc.Contended
class FiledLong2{
public volatile long value=0L;
}
-
用法:既可以用修饰类也可以用来修饰变量。
注意点:@Contended注解只能用于Java的核心类,比如rt包下的类,如果用户的类需要使用这个注解的时候,需要添加JVM的参数:-XX:-RestrictContended,填充的默认宽度为128,要自定义宽度可以设置-XX:ContendedPaddingWidth参数
三、出现伪共享内存的条件
-
在多线程下访问同一个缓存行的多个变量,才会出现伪共享变量的问题,如果在单线程下访问多个变量反而会加速访问。
四、乐观锁和悲观锁
1.悲观锁
-
定义:悲观锁认为外界对数据的修改持保守态度,认为数据很容易被外界修改,在数据被处理之前必须先加锁,这种锁是排他锁,一个线程获取了锁之后,其他线程只能等待或者抛出异常。获取锁的线程,对记录进行操作,然后提交事务释放锁。下面举一个例子
//使用悲观锁来获取
EntryObject entry = query("select * from table1 where id =#{id} for update",id);
//修改记录内容,根据计算修改entry记录的属性
String name=generatorName(entry);
entry.setName(name);
//update操作
int count = updateZ("update table1 set name=#{name},age=#{age} where id=#{id}",entry);
return count;
-
对于上面的代码,使用了事务切面的方法,只要进入这个方法种就开始执行事物一直到这个方法结束,多个线程调用这个方法的时候,只有一个线程能够获取锁,其他线程就会阻塞挂起,直到原线程释放锁 -
乐观锁是相对悲观锁而存在的的方式,一般认为如果只是访问数据那么就是可以不用加锁,只有要更新数据的时候,才会正式对数据冲突与否进行检测,具体来说,根据update返回的行数让用户决定如何去做。将上面的例子改为乐观锁。
package com.ruigege.OtherFoundationOfConcurrent2;
public class UpdateEntry2 {
public int updateEntry(long id) {
//使用乐观锁获取指定记录
EntryObject entry = query("select * from table1 where id=#{id}",id);
//
String name = generatorName(entry);
entry.setName(name);
//update操作
int count = update("update table1 set name=#{name},age=#{age},version=${version}+1 where id=#{id} and version =#{version}",entry);
return count;
}
}
-
对比上面的代码就可以知道根据version来进行更新数据,更新成功的话,就会给version+1,其他线程进行更新的时候,如果vesion不对的话,那么就会停止更新。我们也是使用不断地循环来获取锁而解决更新的问题
package com.ruigege.OtherFoundationOfConcurrent2;
public class updateEntry3 {
boolean result = false;
int retryNum = 5;
while(retryNum>0) {
//使用乐观锁获取记录
EntryObject entry = query("select * from table1 where id=#{id}",id);
String name = generatorName(entry);
entry.setName(name);
//update操作
int count = update("update table1 set name=#{name},age=#{age},version=${version}+1 where id=#{id} and version =#{version}",entry);
//返回的行如果不是0的话说明更新成功了,那么即刻跳出循环
if(count == 1) {
result = true;
break;
}
retryNum--;
}
return result;
}
-
乐观锁并不会使用数据库提供的锁机制,一般在表中添加version字段或者使用业务状态来实现,乐观锁直到提交时才锁定,所以不会产生任何死锁。
五、公平锁和非公平锁
-
公平锁表示线程获取锁的顺序是按照先到先得的原则,非公平锁在运行的时候闯入,也就是不一定先到先得。 -
ReentrantLock提供了公平和非公平锁。
六 、源码:
-
所在包:com.ruigege.OtherFoundationOfConcurrent2 -
https://github.com/ruigege66/ConcurrentJava
-
CSDN:https://blog.csdn.net/weixin_44630050 -
博客园:https://www.cnblogs.com/ruigege0000/
出 处:https://www.cnblogs.com/ruigege0000/p/14077330.html
最新更新
python爬虫及其可视化
使用python爬取豆瓣电影短评评论内容
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
uniapp/H5 获取手机桌面壁纸 (静态壁纸)
[前端] DNS解析与优化
为什么在js中需要添加addEventListener()?
JS模块化系统
js通过Object.defineProperty() 定义和控制对象
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比