只有真正理解了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. B - Yet Another Palindrome Problem

    B - Yet Another Palindrome Problem 思路: 给一个长为n(≤5000≤5000)的数组,问是否存在一个长度至少为3的子序列是回文的.回文的定义是把序列reverse, ...

  2. Camstar获取回参

    public static bool SplitQty(string Username, string Password, string Container, int splitQty,int pla ...

  3. SourceInsight4.0修改字体大小

    SourceInsight4.0修改字体大小 1.右键Options->Preferences.如图所示,然后找到Syntax Decorations选项,点击File Types 2.点击Fi ...

  4. Java基础__04.GUI编程

    GUI编程常见的组件: 窗口 弹窗 面板 文本框 列表框 按钮 图片 监听事件 鼠标操作 键盘事件 GUI简介 GUI的核心技术:Swing AWT,是需要jre环境的. AWT介绍 包含了很多类和接 ...

  5. Oracle重建索引

    创建表 create table student( student_id number, name varchar2(240) ) tablespace school_data; 创建索引 creat ...

  6. OpenLayer——模拟运动轨迹

    模拟在人地图上移动,动态绘制行动轨迹的功能,附带一个跟随的气泡弹窗. <!DOCTYPE html> <html lang="en"> <head&g ...

  7. pycharm永久激活码(亲测好用)

    引用自某不知名大佬,在此致谢,获取激活码链接如下: https://www.ajihuo.com/pycharm/4197.html

  8. springboot 集成poi导出word(一)

    使用ruoyi-前后端分离版本,根据word模板导出,包含表格和图片. 一.创建模板 列表使用{{}},文本使用[] 二.引入依赖 <!-- excel工具 --> <depende ...

  9. nacos 1.4.2 建立集群,公司启动linux服务器常用命令

    2022-7-29 编写micro自动处理脚本,并加入了守护精灵进程 先启动prop1 服务器(xx.1xx.165.186) 再启动prop2服务器 (xx.1xx.174.173) ####### ...

  10. .Net Core WebApi + Vue前后端分离项目中的jwt令牌应用

    1.Start up 里面配置JWT认证方案 //认证方案 services.AddAuthentication(option => { option.DefaultAuthenticateSc ...