简单再介绍一下 synchronized 与 spring 的事务以及两者的配合使用
synchronized
介绍
synchronized 关键字(独占式的悲观锁,同时属于可重入锁)它可以把任意一个非NULL的对象当作锁。
本质上根据属性可以分为对象锁、class锁。
根据使用场景可分为方法锁与代码块锁。
- 方法锁
作用于普通方法时,锁住的是类对象的实例(this);
**作用于静态方法时,锁住的是类Class实例;**又因为Class的相关数据存储在永久带PermGen(jdk1.8 则是 metaspace),永久带是全局共享的,
因此静态方法锁相当于类的一个全局锁,会锁所有调用该方法的线程;
- 代码块锁
synchronized (this) 是对象锁,只对对象生效,spring默认单例,只有一个对象,所以生效,
synchronized ( A.class ) 是类锁,锁class,所有类对象生效
synchronized (Object) 方法,是对象锁,锁实例对象, 静态方法,是类锁对于静态方法,由于此时对象还未生成,所以只能采用类锁;
锁住的是所有以该对象为锁的代码块。它有多个队列,当多个线程一起访问某个对象监视器的时候,对象监视器会将这些线程存储在不同的容器中。
使用
普通方法使用
public synchronized void m1() {
}
静态方法使用
public synchronized static void m0() {
}
代码块中使用类锁
public static void m0() {
synchronized (A.class) {
}
}
代码块中使用对象类锁
public static void m0() {
synchronized (this) {
}
}
代码块中使用对象锁
# 若下面对象lock 是 static 则为全局锁,若为普通对象,则锁当前
private static final Object lock = new Object();
public static void m0() {
synchronized (lock) {
}
}
Spring 事务
日常中我们使用的spring都是用声明式事务,声明式事务由于基于AOP:将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。而Spring AOP 是构建在动态代理的基础上。
事务失效场景
下列场景会失效
- 方法不是public类型的
- 启用spring事务管理功能
- 自身调用问题
- 异常类型错误(默认回滚的是:RuntimeException)
- 异常被吞了(catch)
- 业务和spring事务代码必须在一个线程中
- 数据库引擎不支持事务
- 没有被 Spring 管理
- 数据源没有配置事务管理器
事务类内部方法调用事务不生效问题
现象:
同一个类中 非事务A掉事务B B抛异常 A无事务 B事务不生效
原因:
事务基于aop的代理,仅有外部方法调用过程才会被代理截获,目标方法才由 Spring 生成的代理对象来管理,若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,会失效
解决方案
- AopContext.currentProxy()
- 拆分方法到不同的service
- 注入自己 @Autowired
同步锁与事务配合使用
开发中,有需求为 既有同步性要求,又有事务一致性要求,应该按下面用法
把同步关键字放到controller中,
private static final Object lock = new Object();
public void t1() {
synchronized (lock) {
service.t2();
}
}
service
@Transactional
public void t2() {
//业务
}