【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;具体案例分析可以在博主首页搜索"超卖"

相关推荐
客卿12320 分钟前
滑动窗口--模板
java·算法
zjjsctcdl42 分钟前
java与mysql连接 使用mysql-connector-java连接msql
java·开发语言·mysql
Moe4881 小时前
WebSocket :从浏览器 API 到 Spring 握手、Handler 与前端客户端
java·后端·架构
顶点多余1 小时前
线程互斥+线程同步+生产消费模型
java·linux·开发语言·c++
⑩-1 小时前
Java基础+集合框架-八股文
java·开发语言
福运常在1 小时前
股票数据API(19)次新股池数据
java·python·maven
Zaki_gd1 小时前
Cortex-M7 D-Cache 与 DMA 缓存一致性说明
java·spring·缓存
多看书少吃饭1 小时前
Vue3 + Java + Python 打造企业级大模型知识库(含 SSE 流式对话完整源码)
java·python·状态模式
Arthas2171 小时前
Java大厂面试:从Spring到微服务的全面技术考察
java·jvm·spring·微服务·面试·并发
mifengxing1 小时前
力扣HOT100——(1)两数之和
java·数据结构·算法·leetcode·hot100