对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框 ...
随机推荐
- shell语法6-exit命令、文件重定向、引入外部脚本
一.exit命令 exit命令用来退出当前shell进程,并返回一个退出状态:使用$?可以接收这个退出状态.exit命令可以接受一个整数值作为参数,代表退出状态.如果不指定,默认状态值是 0.exit ...
- iis发布后设置文件夹用户安全权限
发布iis后异常截图: 401 - Unauthorized: Access is denied due to invalid credentials.You do not have permissi ...
- 查看linux 用户
sudo cat /etc/passwd|grep -v nologin|grep -v halt|grep -v shutdown|awk -F":" '{ print $1}' ...
- save an excel csv to a github csv file
:%s/\t/,/g
- QMap 删除指针内容时的一个问题
在测试代码时,发现一个问题: void UserManager::removeUser(const QString &name) { QMap<QString, User*>::I ...
- 初次安装虚拟机和Linux
--初学Linux记录点滴 使用软件 VMware-Workstation-15.0.4 CentOS-7-x86_64-Minimal-2003.iso 1.首先使用 VMware-Workstat ...
- MySQL时区的问题
我这里是在application.properties文件中配置的MySQL连接信息. 开始时间显示不征程是因为没有配置时区,后来加上了setTimeZone=Asia/Shanghai,时间显示正常 ...
- clear_buff-cache.sh
#! /bin/bash sync;echo 1 > /proc/sys/vm/drop_caches # 表示清除pagecache sync;echo 2 > /proc/sys/vm ...
- vue html转pdf并打印
//文件名随便取一个如:htmlToPdf.js // 导出页面为PDF格式 import html2Canvas from 'html2canvas' import JsPDF from 'jspd ...
- 手机安装python环境
一.安装Termux环境 1.下载Termux Qpython 安装以后玩爬虫各种报错,也就不纠结了,直接弄Termux 虚拟环境 下载链接:https://wiki.termux.com/wiki/ ...