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

相关推荐
Lojarro8 分钟前
【Spring】Spring框架之-AOP
java·mysql·spring
莫名其妙小饼干11 分钟前
网上球鞋竞拍系统|Java|SSM|VUE| 前后端分离
java·开发语言·maven·mssql
isolusion23 分钟前
Springboot的创建方式
java·spring boot·后端
zjw_rp1 小时前
Spring-AOP
java·后端·spring·spring-aop
Oneforlove_twoforjob1 小时前
【Java基础面试题033】Java泛型的作用是什么?
java·开发语言
TodoCoder1 小时前
【编程思想】CopyOnWrite是如何解决高并发场景中的读写瓶颈?
java·后端·面试
向宇it1 小时前
【从零开始入门unity游戏开发之——C#篇24】C#面向对象继承——万物之父(object)、装箱和拆箱、sealed 密封类
java·开发语言·unity·c#·游戏引擎
小蜗牛慢慢爬行1 小时前
Hibernate、JPA、Spring DATA JPA、Hibernate 代理和架构
java·架构·hibernate
星河梦瑾2 小时前
SpringBoot相关漏洞学习资料
java·经验分享·spring boot·安全
黄名富2 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua