【spring】spring单例模式与锁对象作用域的分析

前言:spring默认是单例模式,这句话大家应该都不陌生;因为绝大多数都是使用单例模式,避免了某些问题,可能导致对某些场景缺乏思考。本文通过结合lock锁将单例模式、静态变量、锁对象等知识点串联起来。

文章目录

synchronized与ReentrantLock

这两种锁,提得最多的两点区别:

1.效率问题

  1. ReentrantLock可以设置超时时间

当然,还有其它区别 例如ReentrantLock支持公平锁 本文不展开重点讨论,本文重点从第一点延伸。

关于效率问题,主要是旧版jdk6之前 锁很重 ,在jdk6之后优化了很多;可能有同学会说synchronized锁的范围太大了,ReentrantLock锁的范围小,但其实只要我们自己定义一个锁对象即可:

java 复制代码
Object lock = new Object();

public void test(){
 // do something
 synchronized(lock){
   // do something
 }
 // do other
}

从上面代码我们可以看到有个重要的概念:锁对象,通俗点理解就是根据什么对象来判断是否加上了锁。

再看看ReentrantLock的写法:

java 复制代码
ReentrantLock lock = new ReentrantLock();

public void test(){
	// do something
	lock.lock();
	// do other
	lock.unlock();
}

不难发现,ReentrantLock并没有显式的声明锁对象;其实就是lock对象本身,锁的是它自己。

正因为lock.lock() 是锁它本身,所以我们需要思考,当lock对象发生了变化,锁是否还能达到我们的预期。

静态变量

简单复习一下:静态变量存放于jvm的方法区,它只会执行一次,是共享变量。

spring与ReentrantLock整合

我们假设业务场景为:同一时刻 只允许一个用户下单,且为单体项目(即不考虑集群多实例)

简单看一下demo代码:思考从前端/浏览器发起多次请求,打印结果是否一致?

java 复制代码
@RestController
@RequestMapping("/test/single")
public class SingleController {
    private Lock lock  = new ReentrantLock();
    @GetMapping
    public void test() {
        System.out.println(lock.hashCode());
    }
}

答案是一致,因为spring默认是单例模式,所以每次访问的SingleController bean ,都为同一个,bean没重新创建,里面的属性(demo中的lock对象)自然也都是同一个。

如果实在担心hashCode不严谨,或者可能是hash碰撞问题,那我们可以加锁,看多次请求浏览器是不是在等待响应也可以得到相应的结论。

如下代码,超时时间设为10秒

观察浏览器后续请求是否在等待响应(等待锁释放)

我们再来看看反面教材:

java 复制代码
@RestController
@RequestMapping("/test/single")
public class SingleController {
    @GetMapping
    public void test() {
        Lock lock  = new ReentrantLock();
        lock.lock();
        System.out.println("下单---");
        // 省略try catch finally
        lock.unlock();
    }
}

这把锁有作用吗?答案是否定的,因为每次进入到方法里面,生成的都是新对象,锁对象也都变了。

习惯了spring的单例模式,那接下来我们再手动把spring的模式改成多例会发生什么呢:

java 复制代码
@RestController
@RequestMapping("/test/single")
@Scope("prototype")
public class SingleController {
    private Lock lock  = new ReentrantLock();

    @GetMapping
    public void test() {
        System.out.println(lock.hashCode());
    }
}

这把锁还有用吗?答案是否定的,因为每次浏览器发起请求,SingleController 是不同的对象,那lock自然也是不同的对象,所以失去了锁的作用。

那么如果在多例模式下,还想锁生效该怎么办呢?前文提到了静态变量的概念,静态变量初始化后就不会改变(除非我们手动改),那只要将锁对象修饰为static,即可达到效果:

java 复制代码
@RestController
@RequestMapping("/test/single")
@Scope("prototype")
public class SingleController {
    private static Lock lock  = new ReentrantLock();

    @GetMapping
    public void test() {
        System.out.println(lock.hashCode());
    }
}

本文通过一个简单的案例,将锁对象和spring单多例知识点串联,巩固java基础。

生产环境项目加锁方案

在实际的企业项目中,都会集群部署,所以我们需要引入分布式锁,如redis锁,可以基于数据量考虑手写redis锁,或引入redisson;具体案例分析可以在博主首页搜索"超卖"

相关推荐
天天扭码32 分钟前
五天SpringCloud计划——DAY1之mybatis-plus的使用
java·spring cloud·mybatis
程序猿小柒38 分钟前
leetcode hot100【LeetCode 4.寻找两个正序数组的中位数】java实现
java·算法·leetcode
不爱学习的YY酱1 小时前
【操作系统不挂科】<CPU调度(13)>选择题(带答案与解析)
java·linux·前端·算法·操作系统
丁总学Java1 小时前
Maven项目打包,com.sun.tools.javac.processing
java·maven
kikyo哎哟喂2 小时前
Java 代理模式详解
java·开发语言·代理模式
Doker 多克2 小时前
Spring AI 框架使用的核心概念
人工智能·spring·chatgpt
duration~2 小时前
SpringAOP模拟实现
java·开发语言
小码ssim2 小时前
IDEA使用tips(LTS✍)
java·ide·intellij-idea
潜洋2 小时前
Spring Boot教程之五:在 IntelliJ IDEA 中运行第一个 Spring Boot 应用程序
java·spring boot·后端
暮志未晚Webgl3 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5