JVM本地锁失效的三种情况
早睡蛋
# 多例模式
在SpringBoot中,默认的service层以及其他的层都是单例的,操作的都是同一个对象,我们可以自定义多例模式。在类上方加入以下注解即可。
@Scope(value = "prototype",proxyMode = ScopedProxyMode.TARGET_CLASS)
1
我们可以查看ScopedProxyMode代理模式的源码:
public enum ScopedProxyMode {
/**
* Default typically equals {@link #NO}, unless a different default
* has been configured at the component-scan instruction level.
*/
DEFAULT,
/**
* Do not create a scoped proxy.
* <p>This proxy-mode is not typically useful when used with a
* non-singleton scoped instance, which should favor the use of the
* {@link #INTERFACES} or {@link #TARGET_CLASS} proxy-modes instead if it
* is to be used as a dependency.
*/
NO,
/**
* Create a JDK dynamic proxy implementing <i>all</i> interfaces exposed by
* the class of the target object.
*/
INTERFACES,
/**
* Create a class-based proxy (uses CGLIB).
*/
TARGET_CLASS
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
完整代码:
@Service
@Scope(value = "prototype",proxyMode = ScopedProxyMode.TARGET_CLASS)
public class StockService {
private ReentrantLock lock = new ReentrantLock();
@Autowired
private StockMapper stockMapper;
public void deduct() {
lock.lock();
try {
QueryWrapper<Stock> stockQueryWrapper = new QueryWrapper<>();
stockQueryWrapper.eq("product_code", "1001");
Stock stock = stockMapper.selectOne(stockQueryWrapper);
if (stock != null && stock.getCount() > 0) {
stock.setCount(stock.getCount() - 1);
stockMapper.updateById(stock);
}
} finally {
lock.unlock();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Spring默认是JDK代理,SpringBoot2.X使用的是CGLIB代理
测试结果如下,出现并发问题
# 事务
事务的存在也会让JVM本地锁失效
将代码加上事务注解:
@Transactional
public void deduct() {
// lock.lock();
try {
QueryWrapper<Stock> stockQueryWrapper = new QueryWrapper<>();
stockQueryWrapper.eq("product_code", "1001");
Stock stock = stockMapper.selectOne(stockQueryWrapper);
if (stock != null && stock.getCount() > 0) {
stock.setCount(stock.getCount() - 1);
stockMapper.updateById(stock);
}
} finally {
// lock.unlock();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
测试结果如下,锁失效了
# 分析
@Transactional 是AOP思想,步骤:
1.在前置通知内开启事务
2.获取锁
3.查询数据库
4.更新数据库
5.释放锁
6.提交/回滚事务
* 并发情况下:A事务释放锁但没有提交前,B事务进来了
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 集群
集群下,JVM本地锁也会失效,这里用Nginx做一个负载均衡,测试失效案例
nginx.conf配置如下:
upstream ideaProject {
server localhost:9680;
server localhost:9681;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://ideaProject;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
经过压力测试,出现并发问题,证明本地锁失效了: