两种情况分析:1、@Transactional+Synchronized。2、AOP+锁。扩展:分布式锁

point:文章中的内容本人并没有在实际中用过也没有测试过,只是想到了这个问题所以按自己目前的理解做一个记录,正确性待验证。

@Transactional+Synchronized

在使用Spring的@Transactional处理事务时虽然有事务隔离机制,但是在SERIALIZABLE以下的隔离级别并不能解决并发情况下的幻读、脏读问题。看下面的一个例子:

@Transactional

public void update() {

boolean index = select(....);

if(index) {

update( ...index = false...,);

doSomthing()

}

}

@Transactional默认的隔离级别是可重复读,按道理说是没有脏读、不可重复读等问题的,但是细想一下,假如以上代码是为了保证doSomthing()只被执行一次。如果两个线程对应的事务A和事务B都进行了select操作(因为该隔离级别下读不阻塞),返回都为true。然后事务A进行了update并执行了doSomthing()。当A执行完updat,B又会接着往下执行,会再执行一次doSomthing()。

所以就需要业务层来实现严格的同步,Java中锁实现有Synchronized和ReentrantLock两种,代码可能是这个样子的:

private ReentrantLock lock = new ReentrantLock();

@Transactional

public void update() {

lock.lock();

try {

boolean index = select(....);

if(index) {

update( ...index = false...,);

doSomthing()

}

}finally {

lock.unlock();

}

}

------------------------

@Transactional

public synchronized void update() {

boolean index = select(....);

if(index) {

update( ...index = false...,);

doSomthing()

}

}

不管是Synchronized还是ReentrantLock,其实这样写都还是不能保证同步,因为Spring的@Transactional是AOP实现的,它是在函数开始前开启事务,在函数执行完毕后提交事务。所以上面代码中锁的释放是在事务提交之前,所以还是会有前面提到的问题。

AOP+锁

上面提到的锁无效是因为锁的范围是在AOP事务范围之内,那针对这个的解决方案可以使用AOP+锁实现,我们可以自定义一个锁注解:

@Target({ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

public @interface MyLock {

}

然后定义一个AOP切面:

@Component

@Aspect

public class LockAspect implements Ordered {

private static ReentrantLock lock = new ReentrantLock();

@Pointcut("@annotation(MyLock)")

public void dolock(){}

@Around("dolock()")

public void testLock(ProceedingJoinPoint joinPoint) {

lock.lock();

try {

joinPoint.proceed();

} catch (Throwable throwable) {

...

}finally {

lock.unlock();

}

}

public int getOrder() {

return Ordered.LOWEST_PRECEDENCE; //小的先执行

}

}

然后我们就可以这样实现锁同步:

@MyLock

@Transactional

public synchronized void update() {

boolean index = select(....);

if(index) {

update( ...index = false...,);

doSomthing()

}

}

分布式锁

上面的锁实现在分布式环境下都是无效的,因为分布式下lock不是一个对象。我了解的解决方案有下面几个:

1、自己通过数据库实现锁

既然reentranLock和Synchronized不能使用的原因是锁对象不是同一个,那是不是可以通过数据库来实现同一个锁对象?在数据库中定义一个锁表,插入一个状态记录(比如0/1、true/false),当线程读取数据库记录是0的时候就视为获取锁,并将状态修改为1。当业务操作执行完要释放锁的时候就将状态改回0。或者直接利用数据库的唯一性约束,设置唯一主键,每次获取锁的操作都是一个insert操作,因为唯一性约束所以只会有一个线程insert成功,即获得锁,释放锁时delete该条记录就行。

this.SQL_TRY_ACQUIRE_LOCK = "update " + props.getTableName() + " set locked = true ,token = ? ,start_time = ? ,expire_time = ? ,end_time = null

where id = ? and (locked = false or expire_time < ?) ";

this.SQL_SELECT_LOCK = "select id ,name ,locked ,token ,start_time ,end_time ,expire_time ,attributes from " + props.getTableName() + "

where id = ? ";

public DLock acquireLock(DLockType type, String lockToken, long currTime, long expireTime) {

this.template.update(this.SQL_TRY_ACQUIRE_LOCK, new Object[]{lockToken, new Timestamp(currTime), new Timestamp(expireTime), type.getId(), new Timestamp(currTime)});

List locks = this.template.query(this.SQL_SELECT_LOCK, new Object[]{type.getId()}, this.rowMapper);

if (locks != null && !locks.isEmpty()) {

DLock lock = (DLock)locks.get(0);

if (lockToken.equals(lock.getLockToken())) {

return lock;

} else {

long sysCurrTime = System.currentTimeMillis();

if (!lock.isLocked() || lock.getExpireTime() != null && lock.getExpireTime() >= currTime) {

return null;

} else {

logger.debug("LockExpireTime={}, CurrTime={}, SysCurrTime={}", new Object[]{lock.getExpireTime(), currTime, sysCurrTime});

return lock;

}

}

} else {

return null;

}

}

以上代码先update,如果锁空闲就可以update成功,并且update的时候数据库锁会保证其他线程不能操作该条数据。

2、利用缓存如redis实现

3、利用zookeeper实现

java进账和转账需要锁吗,Spring与Java中的锁相关推荐

  1. @Transactional事务中使用锁坑(@Transactional事务中使用锁失效)

    @Transactional事务中使用锁失效 说明: Spring中使用注解@Transactional作事务管理,@Transactional注解在方法上时,是方法完成之后才进行提交事务的 测试代码 ...

  2. mysql 高并发写入锁表_使用mysql中的锁解决高并发问题

    阿里云产品通用代金券,最高可领1888分享一波阿里云红包. 阿里云的购买入口 为什么要加锁 多核计算机的出现,计算机实现真正并行计算,可以在同一时刻,执行多个任务.在多线程编程中,因为线程执行顺序不可 ...

  3. 锁失效_关于bigtable中chubby锁失效时的一点思考

    最近跟国内几家热门公司做分布式存储的大佬们聊了聊,过程十分愉快,但同时也有点小虐.说到底,自己在这个领域并没有很久的经验,很多东西仍停留在知其然而不知其所以然的地步.魔鬼藏在细节之处. 不过这也正好是 ...

  4. 不属于linux内核锁的是,Linux内核中的锁

    1. 为什么要保证原子性 处理器分两种:cisc(复杂指令集,可以直接在内存上进行操作,如x86,一条汇编指令可以原子的完整读内存.计算.写内存)和rics(精简指令集,所有操作都必须是在CPU内部进 ...

  5. java aop面试_我想知道Spring在面试中应该怎么介绍,以及如何介绍他的aop?

    Spring是一个开源框架,它由Rod Johnson创建.它是为了解决企业应用开发的复杂性而创建的.Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情.然而,Spring的用途 ...

  6. java mvc 批量插入_请教mysql spring mvc +mybatis中批量插入的问题?

    dao实现文件中函数: @Override public int insertContentList( List list) { Map params = createMap(); params.pu ...

  7. Java中的锁原理、锁优化、CAS、AQS详解

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:景小财 www.jianshu.com/p/e674ee68 ...

  8. Spring Boot Redis 实现分布式锁,真香!!

    之前看很多人手写分布式锁,其实 Spring Boot 现在已经做的足够好了,开箱即用,支持主流的 Redis.Zookeeper 中间件,另外还支持 JDBC. 本篇栈长以 Redis 为例(这也是 ...

  9. Spring Boot Redis 实现分布式锁,真香

    之前看很多人手写分布式锁,其实 Spring Boot 现在已经做的足够好了,开箱即用,支持主流的 Redis.Zookeeper 中间件,另外还支持 JDBC. 本篇栈长以 Redis 为例(这也是 ...

  10. Java中的锁[原理、锁优化、CAS、AQS]

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:用好Java中的枚举,真的没有那么简单!个人原创+1博客:点击前往,查看更多 作者:高广超 链接:https:/ ...

最新文章

  1. 北风设计模式课程---里氏替换原则(Liskov Substitution Principle)
  2. 鼠标滚轮事件及解决滚轮事件多次触发问题
  3. 在FF与IE中使用数据岛
  4. 太突然!一日本上班族大叔被通知得了诺贝尔奖,他却选择消失了16年,又有重大发现!...
  5. 前端(jQuery)(10)-- jQuery标签切换
  6. 你为什么选择计算机应用专业,致新生!我为什么选择信息工程系
  7. HDU 4031 Attack(线段树/树状数组区间更新单点查询+暴力)
  8. 我碰到的到现在为止,还没有找到比较好的解决方法的sps问题
  9. Linux 以form表单形式上传文件
  10. win10哪个版本打游戏好?win10游戏性能分析
  11. 记录vant weapp 小程序组件库遇到的坑以及ios和安卓兼容问题 SubmitBar
  12. Bundle Adjustment — A Modern Synthesis(一)
  13. [论文阅读] (22)图神经网络及认知推理总结和普及-清华唐杰老师
  14. 85 R 银行信用卡风控评分数据分析
  15. DHCP服务配置-Cisco模拟器
  16. Windows环境下安装pkg-config
  17. html页面转盘如何实现,html5制作转盘的详解及实例
  18. (1146, Table 'django.django_session' doesn't exist)
  19. 【经验分享】为什么视频画面解码失败之后显示的是绿幕?
  20. 黑马视频~rocketMq

热门文章

  1. lightoj 1029 最小生成树 + 最大生成树
  2. 手把手教你在Windows下搭建React Native Android开发环境
  3. Head First Design Patterns(深入浅出设计模式)-设计模式介绍
  4. Mybatis之插入ListT
  5. 关于 LimitedConcurrencyLevelTaskScheduler 的疑惑
  6. ubuntu安装dockers和images:dvwa
  7. inner join ,left join ,right join区别
  8. Android消息推送(Android Push Notification)
  9. win8下Oracle 12c 创建新用户并分配表空间
  10. HTML5手机游戏将迎美好未来 .