对synchronized的理解和Spring为什么是单例的
只有真正理解了Java中对象是什么,才能理解这个关键字是什么意思
字面解释
Java Guide中如此解释:
synchronized 关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。
测试
但是这句话很多时候是有误导性的,synchronized这个关键字并不能保证同一时间只有一个线程访问,确切说,如果是用同一个对象调用方法的时候,方法的确是同一时间只能有一个线程访问:
class Solution {
public static void main(String[] args) throws Exception {
Node node1 = new Node();
Node node2 = new Node();
new Thread(node1::test).start();
new Thread(node1::test).start();
System.out.println("主线程结束");
}
}
class Node {
public synchronized void test() {
System.out.println("!!!!!!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("完成!!!");
}
}
输出为
!!!!!!
主线程结束
完成!!!
!!!!!!
完成!!!
没有问题,一个时间走一个线程。
但是,如果你用两个不同的对象调用同一个方法,synchronized关键字是无用的:
class Solution {
public static void main(String[] args) throws Exception {
Node node1 = new Node();
Node node2 = new Node();
new Thread(node1::test).start();
new Thread(node2::test).start();
System.out.println("主线程结束");
}
}
class Node {
public synchronized void test() {
System.out.println("!!!!!!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("完成!!!");
}
}
输出为:
!!!!!!
!!!!!!
主线程结束
完成!!!
完成!!!
这时候为什么同步监视器没用了呢?原因在于,synchronized加在普通方法的时候,当一个线程访问这个方法的时候,持有的是当前类实例对象的同步监视器,也就是说当node1调用test()的时候,node1本身被他自己的线程new Thread持有了。这时候如果node2再次调用test(),由于node2自己没有被任何线程持有,所以synchronized此时是失效的。如果是用node1对象调用调用了test(),这时node1被线程1持有以后,第二个new出来的线程是无法持有node1的,就只能等待。
所以一切都基于对Java“对象”这个概念的理解。
当synchronized加在静态方法上的时候,线程持有的是类的Class对象:
class Solution {
public static void main(String[] args) throws Exception {
Node node1 = new Node();
Node node2 = new Node();
// new Thread(node1::test).start();
// new Thread(node2::test).start();
new Thread(() -> {
node1.test();
}).start();
new Thread(() -> {
node2.test();
}).start();
System.out.println("主线程结束");
}
}
class Node {
public static synchronized void test() {
System.out.println("!!!!!!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("完成!!!");
}
}
这种情况下,即便两次调用test()方法属不同的对象,但是,由于线程持有的clazz对象是单例的,所以依然达到了同步的效果:
!!!!!!
主线程结束
完成!!!
!!!!!!
完成!!!
对于同步代码块,也是一样的操作,一般情况下我们会将同步代码块中传入this,就类似于将方法调用者的对象作为了同步监视器,这样的操作在单例模式的基础上是可以达到同步效果的。
Spring为什么是单例的
所以从这里可以看出,Spring为什么会把组件都设置为单例的呢?一方面Spring中各个组件的功能实现不需要多实例,请求和请求之间方法调用多为无状态的,当多个查询数据库的请求调用到DAO层的时候,自然有mybatis帮我们实现代理类的不同对象去隔离不同请求的数据,在Controller和Service构建多实例对象浪费内存空间;另一方面,单例是有助于实现同步效果的。当我们在控制器接口的方法声明为synchronized,这时用这个controller调用这个方法的时候,默认是用controller对象自己作为同步监视器的,而controller对象自然是满足单例的,这样就自然满足了同步的要求。
对synchronized的理解和Spring为什么是单例的的更多相关文章
- spring如何解决单例循环依赖问题?
更多文章点击--spring源码分析系列 1.spring循环依赖场景2.循环依赖解决方式: 三级缓存 1.spring循环引用场景 循环依赖的产生可能有很多种情况,例如: A的构造方法中依赖了B的实 ...
- Spring对象类型——单例和多例
由于看淘淘商城的项目,涉及到了项目中处理spring中bean对象的两种类型,分别是单例和多例,就在此记录一下,方便加深理解,写出更加健壮的代码. 一.单例和多例的概述 在Spring中,bean可以 ...
- Spring bean 和单例bean的线程安全
Bean的作用域 Spring 3中为Bean定义了5中作用域,分别为singleton(单例).prototype(原型).request.session和global session,5种作用域说 ...
- Spring中的单例一二
Spring框架很好的帮助我们创建和管理dao.bean.service.action等对象, 但是它创建的对象是单例呢还是多例,又有哪些区别以及为什么 1.在Spring中默认创建的是单例模式,简单 ...
- 转:【Spring MVC Controller单例陷阱】
http://lavasoft.blog.51cto.com/62575/1394669/ Spring MVC Controller默认是单例的: 单例的原因有二:1.为了性能.2.不需要多例. 1 ...
- Spring MVC Controller单例陷阱
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://lavasoft.blog.51cto.com/62575/1394669 Spr ...
- 不使用synchronized和lock 锁实现线程安全单例
单例实现方式一,锁机制 public class Singleton { private static Singleton singleton=null; public Singleton() { } ...
- spring中的单例和多例
单例 对象在整个系统中只有一份,所有的请求都用一个对象来处理,如service和dao层的对象一般是单例的. 为什么使用单例:因为没有必要每个请求都新建一个对象的时候,浪费CPU和内存. 多例 对象在 ...
- Spring IOC 容器源码分析 - 创建单例 bean 的过程
1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...
- Spring单例Bean和线程安全
Spring的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢?例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框 ...
随机推荐
- Pytorch实战学习(一):用Pytorch实现线性回归
<PyTorch深度学习实践>完结合集_哔哩哔哩_bilibili P5--用Pytorch实现线性回归 建立模型四大步骤 一.Prepare dataset mini-batch:x.y ...
- sentinel监控数据持久化&本地测试
官方文档 https://sentinelguard.io/zh-cn/ wiki: 在生产环境中使用-Sentinel 推荐方案:持久化到 时序数据库InfluxDB : 结合Grafana 可 ...
- Neural Network模型复杂度之Dropout - Python实现
背景介绍 Neural Network之模型复杂度主要取决于优化参数个数与参数变化范围. 优化参数个数可手动调节, 参数变化范围可通过正则化技术加以限制. 本文从优化参数个数出发, 以dropout技 ...
- MySQL---MGR保姆版
一.环境清理: 三台机器都做: 1.修改主机名 2.修改/etc/hosts文件 3.关闭和禁用防火墙 4.关闭和禁用SELinux 5.生成密钥对 6.传输密钥对 7.验证免密登陆 yum remo ...
- 传递数据(props)
student.vue <template> <div> <h3>{{msg}}</h3> <h3>姓名:{{name}}</h3&g ...
- 百题计划-3 codeforces 657div2 C. Choosing flowers 贪心+枚举+二分
https://codeforces.com/contest/1379/problem/C 题意: 给m种花(a,b),从中取出n朵,每种花可以取0和无限朵,如果取出第i朵花的个数为c>0,则贡 ...
- Review1(C#语言基础)
MeshFilter决定了物体时什么形状 MeshRender决定了物体时的外观: 运行时常量:readonly 1.readonly string NAME_READONLY = "rea ...
- vue高级进阶( 一 ) 组件精髓概述
前言 这个系列可能会分为几部分: 基础以及高级用法总结 一些比较有代表性的实战 源码解析(一定是用最粗俗,不对,是最通俗的语言讲解,这个我可以保证) 总之一定对得起高级进阶这几个字... 组件分类 v ...
- Andorid 悬浮窗的适配指北
我们有时候需要显示一个悬浮窗,悬浮窗由服务开启,里面会有一些操作 但是我在开发时遇到高版本 Android12 和低版本4.4 ,显示不一样的地方 这边整理了下适配的方法方案 一:根据版本不一的时候, ...
- python获取当前运行函数名
两种方式: #!/usr/bin/env python3#coding:utf-8 import sys, inspectdef test_a(): print('func name: ', sys. ...