只有真正理解了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为什么是单例的的更多相关文章

  1. spring如何解决单例循环依赖问题?

    更多文章点击--spring源码分析系列 1.spring循环依赖场景2.循环依赖解决方式: 三级缓存 1.spring循环引用场景 循环依赖的产生可能有很多种情况,例如: A的构造方法中依赖了B的实 ...

  2. Spring对象类型——单例和多例

    由于看淘淘商城的项目,涉及到了项目中处理spring中bean对象的两种类型,分别是单例和多例,就在此记录一下,方便加深理解,写出更加健壮的代码. 一.单例和多例的概述 在Spring中,bean可以 ...

  3. Spring bean 和单例bean的线程安全

    Bean的作用域 Spring 3中为Bean定义了5中作用域,分别为singleton(单例).prototype(原型).request.session和global session,5种作用域说 ...

  4. Spring中的单例一二

    Spring框架很好的帮助我们创建和管理dao.bean.service.action等对象, 但是它创建的对象是单例呢还是多例,又有哪些区别以及为什么 1.在Spring中默认创建的是单例模式,简单 ...

  5. 转:【Spring MVC Controller单例陷阱】

    http://lavasoft.blog.51cto.com/62575/1394669/ Spring MVC Controller默认是单例的: 单例的原因有二:1.为了性能.2.不需要多例. 1 ...

  6. Spring MVC Controller单例陷阱

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://lavasoft.blog.51cto.com/62575/1394669 Spr ...

  7. 不使用synchronized和lock 锁实现线程安全单例

    单例实现方式一,锁机制 public class Singleton { private static Singleton singleton=null; public Singleton() { } ...

  8. spring中的单例和多例

    单例 对象在整个系统中只有一份,所有的请求都用一个对象来处理,如service和dao层的对象一般是单例的. 为什么使用单例:因为没有必要每个请求都新建一个对象的时候,浪费CPU和内存. 多例 对象在 ...

  9. Spring IOC 容器源码分析 - 创建单例 bean 的过程

    1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...

  10. Spring单例Bean和线程安全

    Spring的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢?例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框 ...

随机推荐

  1. Java基础学习:7、作用域

    1.在Java中,主要的变量就是属性(成员变量)和局部变量. 2.我们说的局部变量一般是指在成员方法中定义的变量. 3.Java作用域分类: 全局变量:作用域为整个类,该类中的方法可以使用. 局部变量 ...

  2. Android版本历史

    版本 Code Name 代号 API 日期 NDK 1.0 Astro 铁臂阿童木  1     1.1 Bender, Petit Four 发条机器人 2     1.5 Cupcake  纸杯 ...

  3. 杂:使用Shell判断文件换行符(LF/CRLF)

    前提:文件最后一行有换行符 第一步:以二进制方式取得文件最后两个byte.last2=`tail -c 2 <your_file> | od -x -A n` 第二步:判断最后两个byte ...

  4. ORA-01653:unable to extend table xxxxx by 8192 in tablespace xxxxx

    原因:表空间满 解决:扩展表空间 报错截图: 参考摘录:https://blog.csdn.net/qq_35257875/article/details/90295272

  5. esp8266 -rtos-sdk-vscode-config

    kiteconfig 工作区配置文件:hello_world.code-workspace {      "folders": [         {             &q ...

  6. docker 操作常用命令

    镜像 #以tomcat为基础镜像创建一个容器,容器名为my-tomcat #拉取tomcat最新镜像,实际生产中,docker pull 这一步可以省略,docker run的时候会自己去拉取. do ...

  7. thirty-two(模型点击展示)react-three-fiber

    模型点击蒙版展示 点击展示目的(用户需要看见模型中更加多的内容信息) 使用技术 ThreeJs.React-three-fiber.React-three-drei.React.css 整体思路:   ...

  8. Cesium 结合Echarts绘制航线图

    <template> <div id="cesiumContainer"></div> </template> <script ...

  9. CDO学习2 CDO 入门教程Tutorial

    #20210117#注:如果需要用cdo对数据进行截取,可参考buguse的几篇博文: 如何利用CDO从数据集中提取数据 CDO条件性选择数据 - 云+社区 - 腾讯云 CDO转换数据集格式 - 云+ ...

  10. 三星电子的KNOX安全技术难以满足企业BYOD控管需求

    不可否认的是三星在智能手机领域里的辉煌战绩,三星最近推出了端到端的基于Android平台的解决方案KNOX,并且宣称可以提供从硬件到应用层的多重安全加固. KNOX是否能够延续其在企业领域里辉煌呢?相 ...