精华内容
下载资源
问答
  • 因为成员变量是存放在堆内存中,而堆内存又是线程共享的,这就造成了线程安全问题因为Spring中的Bean默认是单例的,所以在定义成员变量时也有可能会发生线程安全问题。下面我们就来研究下如何解决Spring中单例Bean的...

    首先我们应该知道线程安全问题一般发生在成员变量上,这是为什么啦?

    因为成员变量是存放在堆内存中,而堆内存又是线程共享的,这就造成了线程安全问题

    因为Spring中的Bean默认是单例的,所以在定义成员变量时也有可能会发生线程安全问题。下面我们就来研究下如何解决Spring中单例Bean的线程安全问题

    @RestController

    //@Scope("prototype")

    public class BeanController {

    private int content=0; //基本类型 线程不安全

    private String test=null;//引用类型 线程不安全

    @RequestMapping("testBean")

    public Object getSercurity(){

    System.out.println(content);

    System.out.println(test);

    content=20;

    test="单例模式是不安全的";

    return test;

    }

    问题来了,我们该如何测试线程不安全问题啦?我们需要在程序中用debug模式去启动,打断点。不需要执行完程序,然后再次调用该接口。或者多次调用该接口,便会出现以下控制台所示的结果。

    0a031e3f2792d9f557fba7b5dbb350c2.png

    下面我们就来讨论下解决这个线程不安全的问题的办法

    解决方式一:

    在对应的类名上加上该注解@Scope("prototype"),表示每次调用该接口都会生成一个新的Bean。下图示例

    fffb42515c69667cb6b4f9c759b63585.png

    解决方案二 ThreadLocal解决问题

    @RestController

    //@Scope("prototype")

    public class BeanController {

    private static ThreadLocal content = new ThreadLocal() {

    @Override

    protected Integer initialValue() {

    return (int)(Math.random()*10+100);

    }

    };

    private static ThreadLocal test = new ThreadLocal() {

    @Override

    protected String initialValue() {

    return "单例模式是不安全的"+(int)(Math.random()*10+100);

    }

    };

    @RequestMapping("testBean")

    public Object getSercurity(){

    System.out.println(content.get());

    System.out.println(test.get()); System.out.println();

    return test.get();

    }

    }

    第三种解决方案:

    尽量不要使用成员变量

    第四种解决方案:

    前提:

    该程序是web应用,可以使用Spring Bean的作用域中的request,就是说在类前面加上@Scope("request"),表明每次请求都会生成一个新的Bean对象。

    作用于@Scope("prototype")类似。

    补充知识:SpringMVC是单例的,高并发情况下,如何保证性能的?

    首先在大家的思考中,肯定有影响的,你想想,单例顾名思义:一个个排队过... 高访问量的时候,你能想象服务器的压力了... 而且用户体验也不怎么好,等待太久~

    实质上这种理解是错误的,Java里有个API叫做ThreadLocal,spring单例模式下用它来切换不同线程之间的参数。用ThreadLocal是为了保证线程安全,实际上ThreadLoacal的key就是当前线程的Thread实例。单例模式下,spring把每个线程可能存在线程安全问题的参数值放进了ThreadLocal。这样虽然是一个实例在操作,但是不同线程下的数据互相之间都是隔离的,因为运行时创建和销毁的bean大大减少了,所以大多数场景下这种方式对内存资源的消耗较少,而且并发越高优势越明显。

    总的来说就是,单利模式因为大大节省了实例的创建和销毁,有利于提高性能,而ThreadLocal用来保证线程安全性。

    另外补充说一句,单例模式是spring推荐的配置,它在高并发下能极大的节省资源,提高服务抗压能力。spring IOC的bean管理器是“绝对的线程安全”。

    以上这篇Spring如何解决单例bean线程不安全的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

    展开全文
  • 首先直接给出答案:不是线程安全的 一、分析问题 证明不是线程安全的案例如下: public class Student { private String stuName; public String report(String uname){ stuName = "大家好,我叫:"+uname; try...

    首先直接给出答案:不是线程安全的

    一、分析问题

    证明不是线程安全的案例如下:

    public class Student {
        private String stuName;
    
        public String report(String uname){
            stuName = "大家好,我叫:"+uname;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return stuName;
        }
    }
    -----------------------------------------------------------------------------------------------------------------
    public class Run {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
            Student bean1 = context.getBean(Student.class);
    
            new Thread(() -> {
                System.out.println(bean1.report("张三"));
            }).start();
    
            Student bean2 = context.getBean(Student.class);
            new Thread(() -> {
                System.out.println(bean2.report("李四"));
            }).start();
    
        }
    }
    

    请添加图片描述
    分析原因:线程一执行完stuName的赋值后进入休眠,线程二这时候也进入该方法对stuName进行赋值,由于对象是单例的,线程二的赋值操作也就影响了线程一的打印结果。导致最后打印的结果都是线程二传入的值。

    二、解决方法

    既然单例bean不是线程安全的,那么该怎么解决上面的问题呢?下面博主给出四种解决方法仅供读者参考:

    1.方法一:将成员变量放入方法中

    修改后的Student类如下:

    public class Student {
    //    private String stuName;
    
        public String report(String uname){
            String stuName = "大家好,我叫:"+uname;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return stuName;
        }
    }
    

    2.方法二:加锁使方法串行执行

    比如下面的方法中我加入了synchronized锁:

    public class Student {
        private String stuName;
    
        public synchronized String report(String uname){
            stuName = "大家好,我叫:"+uname;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return stuName;
        }
    }
    

    3.方法三:将bean变成原型模式

    比如加上Scope注解声明为多例模式:

    	@Bean
        @Scope("prototype")
        public Student student(){
            return new Student();
        }
    

    4.方法四:使用ThreadLocal

    改造后的代码如下:

    public class Student {
        private ThreadLocal<String> stuName = new ThreadLocal<>();
    
        public String report(String uname){
            stuName.set("大家好,我叫:"+uname);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return stuName.get();
        }
    }
    
    展开全文
  • 如果Bean是有状态的,那么就需要开发人员自己来进行线程安全的保证,最简单的办法就是改变bean的作用域,把singleton改为property,这样每次请求Bean就相当于是new Bean(),这样就可以保证线程安全了。 有状态就是...

    Spring中的Bean默认是单例模式的,框架并没有对bean进行多线程的封装处理。

    如果Bean是有状态的,那么就需要开发人员自己来进行线程安全的保证,最简单的办法就是改变bean的作用域,把singleton改为property,这样每次请求Bean就相当于是new Bean(),这样就可以保证线程安全了。

    • 有状态就是有数据存储功能
    • 无状态就是不会保存数据,controller,service和dao本身不是线程安全的,只是如果只是调用里面的方法,而且多线程调用一个实例的方法,会在内存中复制变量,这是自己的线程的工作内存,是安全的。

    Dao会操作数据库Connection,Connection是带有状态的,比如数据库事务,Spring的事务管理器使用ThreadLocal为不同线程维护了一套独立的Connection副本,保证线程之间不会互相影响。

    不要在bean中声明任何有状态的实例变量或者类变量,如果必须如此,那么就使用ThreadLocal把变量变为线程私有的,如果bean的实例变量或类变量需要在多个线程之间共享,那么就只能使用synchronized,lock,CAS等这些实现线程同步的方法了。

    展开全文
  • Spring容器中的Bean是否线程安全,容器本身并没有提供Bean线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。 Spring 的 bean 作用域(scope)...

    结论: 不是线程安全的

    Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。

    Spring 的 bean 作用域(scope)类型
      1、singleton:单例,默认作用域。

      2、prototype:原型,每次创建一个新对象。

      3、request:请求,每次Http请求创建一个新对象,适用于WebApplicationContext环境下。

      4、session:会话,同一个会话共享一个实例,不同会话使用不用的实例。

      5、global-session:全局会话,所有会话共享一个实例。

    线程安全这个问题,要从单例与原型Bean分别进行说明。

    原型Bean
      对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。

    单例Bean
      对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。

      如果单例Bean是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。

    spring单例,为什么controller、service和dao确能保证线程安全?

      Spring中的Bean默认是单例模式的,框架并没有对bean进行多线程的封装处理。
      实际上大部分时间Bean是无状态的(比如Dao) 所以说在某种程度上来说Bean其实是安全的。
      但是如果Bean是有状态的 那就需要开发人员自己来进行线程安全的保证,最简单的办法就是改变bean的作用域 把 "singleton"改为“protopyte” 这样每次请求Bean就相当于是 new Bean() 这样就可以保证线程的安全了。

      有状态就是有数据存储功能
      无状态就是不会保存数据

      

      controller、service和dao层本身并不是线程安全的,只是如果只是调用里面的方法,而且多线程调用一个实例的方法,会在内存中复制变量,这是自己的线程的工作内存,是安全的。

      想理解原理可以看看《深入理解JVM虚拟机》,2.2.2节:

    Java虚拟机栈是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
    

      《Java并发编程实战》第3.2.2节:

    局部变量的固有属性之一就是封闭在执行线程中,它们位于执行线程的栈中,其他线程无法访问这个栈。

      所以其实任何无状态单例都是线程安全的。
      Spring的根本就是通过大量这种单例构建起系统,以事务脚本的方式提供服务

    也可以看看这篇加深理解: 关于Spring的@Controller @Service等的线程安全问题

    首先问@Controller @Service是不是线程安全的?
      

    答:默认配置下不是的,为啥呢?因为默认情况下@Controller没有加上@Scope,没有加@Scope就是默认值singleton,单例的。意思就是系统只会初始化一次Controller容器,所以每次请求的都是同一个Controller容器,当然是非线程安全的。举个例子:

    @RestController
    public class TestController {
    
        private int var = 0;
        
        @GetMapping(value = "/test_var")
        public String test() {
            System.out.println("普通变量var:" + (++var));
            return "普通变量var:" + var ;
        }
    }

    在postman里面发三次请求,结果如下:

    1

    2

    3

    普通变量var:1

    普通变量var:2

    普通变量var:3

    说明他不是线程安全的。怎么办呢?可以给他加上上面说的@Scope注解,如下:  

    @RestController
    @Scope(value = "prototype") // 加上@Scope注解,他有2个取值:单例-singleton 多实例-prototype
    public class TestController {
    
        private int var = 0;
        
        @GetMapping(value = "/test_var")
        public String test() {
            System.out.println("普通变量var:" + (++var));
            return "普通变量var:" + var ;
        }
    }

    这样一来,每个请求都单独创建一个Controller容器,所以各个请求之间是线程安全的,三次请求结果:

    1

    2

    3

    普通变量var:1

    普通变量var:1

    普通变量var:1

    加了@Scope注解多的实例prototype是不是一定就是线程安全的呢?  

    @RestController
    @Scope(value = "prototype") // 加上@Scope注解,他有2个取值:单例-singleton 多实例-prototype
    public class TestController {
        private int var = 0;
        private static int staticVar = 0;
    
        @GetMapping(value = "/test_var")
        public String test() {
            System.out.println("普通变量var:" + (++var)+ "---静态变量staticVar:" + (++staticVar));
            return "普通变量var:" + var + "静态变量staticVar:" + staticVar;
        }
    }

    看三次请求结果:

    1

    2

    3

    普通变量var:1---静态变量staticVar:1

    普通变量var:1---静态变量staticVar:2

    普通变量var:1---静态变量staticVar:3

      虽然每次都是单独创建一个Controller但是扛不住他变量本身是static的呀,所以说呢,即便是加上@Scope注解也不一定能保证Controller 100%的线程安全。所以是否线程安全在于怎样去定义变量以及Controller的配置。所以来个全乎一点的实验,代码如下:  

    @RestController
    @Scope(value = "singleton") // prototype singleton
    public class TestController {
    
        private int var = 0; // 定义一个普通变量
    
        private static int staticVar = 0; // 定义一个静态变量
    
        @Value("${test-int}")
        private int testInt; // 从配置文件中读取变量
    
        ThreadLocal<Integer> tl = new ThreadLocal<>(); // 用ThreadLocal来封装变量
    
        @Autowired
        private User user; // 注入一个对象来封装变量
    
        @GetMapping(value = "/test_var")
        public String test() {
            tl.set(1);
            System.out.println("先取一下user对象中的值:"+user.getAge()+"===再取一下hashCode:"+user.hashCode());
            user.setAge(1);
            System.out.println("普通变量var:" + (++var) + "===静态变量staticVar:" + (++staticVar) + "===配置变量testInt:" + (++testInt)
                    + "===ThreadLocal变量tl:" + tl.get()+"===注入变量user:" + user.getAge());
            return "普通变量var:" + var + ",静态变量staticVar:" + staticVar + ",配置读取变量testInt:" + testInt + ",ThreadLocal变量tl:"
                    + tl.get() + "注入变量user:" + user.getAge();
        }
    }

    补充Controller以外的代码:
    config里面自己定义的Bean:User

    @Configuration
    public class MyConfig {
        @Bean
        public User user(){
            return new User();
        }
    }

    我暂时能想到的定义变量的方法就这么多了,三次http请求结果如下:

    1

    2

    3

    4

    5

    6

    先取一下user对象中的值:0===再取一下hashCode:241165852

    普通变量var:1===静态变量staticVar:1===配置变量testInt:1===ThreadLocal变量tl:1===注入变量user:1

    先取一下user对象中的值:1===再取一下hashCode:241165852

    普通变量var:2===静态变量staticVar:2===配置变量testInt:2===ThreadLocal变量tl:1===注入变量user:1

    先取一下user对象中的值:1===再取一下hashCode:241165852

    普通变量var:3===静态变量staticVar:3===配置变量testInt:3===ThreadLocal变量tl:1===注入变量user:1

      可以看到,在单例模式下Controller中只有用ThreadLocal封装的变量是线程安全的。为什么这样说呢?我们可以看到3次请求结果里面只有ThreadLocal变量值每次都是从0+1=1的,其他的几个都是累加的,而user对象呢,默认值是0,第二交取值的时候就已经是1了,关键他的hashCode是一样的,说明每次请求调用的都是同一个user对象。
    下面将TestController 上的@Scope注解的属性改一下改成多实例的:@Scope(value = "prototype"),其他都不变,再次请求,结果如下:
     

    1

    2

    3

    4

    5

    6

    先取一下user对象中的值:0===再取一下hashCode:853315860

    普通变量var:1===静态变量staticVar:1===配置变量testInt:1===ThreadLocal变量tl:1===注入变量user:1

    先取一下user对象中的值:1===再取一下hashCode:853315860

    普通变量var:1===静态变量staticVar:2===配置变量testInt:1===ThreadLocal变量tl:1===注入变量user:1

    先取一下user对象中的值:1===再取一下hashCode:853315860

    普通变量var:1===静态变量staticVar:3===配置变量testInt:1===ThreadLocal变量tl:1===注入变量user:1

      分析这个结果发现,多实例模式下普通变量,取配置的变量还有ThreadLocal变量都是线程安全的,而静态变量和user(看他的hashCode都是一样的)对象中的变量都是非线程安全的。也就是说尽管TestController 是每次请求的时候都初始化了一个对象,但是静态变量始终是只有一份的,而且这个注入的user对象也是只有一份的。静态变量只有一份这是当然的咯,那么有没有办法让user对象可以每次都new一个新的呢?当然可以:
     

    public class MyConfig {
        @Bean
        @Scope(value = "prototype")
        public User user(){
            return new User();
        }    
    }

    在config里面给这个注入的Bean加上一个相同的注解@Scope(value = "prototype")就可以了,再来请求一下看看:

    1

    2

    3

    4

    5

    6

    先取一下user对象中的值:0===再取一下hashCode:1612967699

    普通变量var:1===静态变量staticVar:1===配置变量testInt:1===ThreadLocal变量tl:1===注入变量user:1

    先取一下user对象中的值:0===再取一下hashCode:985418837

    普通变量var:1===静态变量staticVar:2===配置变量testInt:1===ThreadLocal变量tl:1===注入变量user:1

    先取一下user对象中的值:0===再取一下hashCode:1958952789

    普通变量var:1===静态变量staticVar:3===配置变量testInt:1===ThreadLocal变量tl:1===注入变量user:1

    可以看到每次请求的user对象的hashCode都不是一样的,每次赋值前取user中的变量值也都是默认值0。
    下面总结一下:

      1、在@Controller/@Service等容器中,默认情况下,scope值是单例-singleton的,也是线程不安全的。
      2、尽量不要在@Controller/@Service等容器中定义静态变量,不论是单例(singleton)还是多实例(prototype)他都是线程不安全的。
      3、默认注入的Bean对象,在不设置scope的时候他也是线程不安全的。
      4、一定要定义变量的话,用ThreadLocal来封装,这个是线程安全的

    展开全文
  • 单例模式:Bean默认为单例模式。 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术; 模板方法:用来解决代码重复的问题。比如.jdbcTemplate 观察者模式:定义对象键一种一对多的依赖关系,当一...
  • Spring框架中的单例Bean线程安全的么? Spring中的Bean对象默认是单例的,框架并没有对bean进行多线程的封装处理 如果Bean是有状态的,那么就需要开发人员自己来保证线程安全的保证,最简单的办法就是改变bean的...
  • 如果bean是有状态的,那么就需要开发人员自己进行线程安全的保证,最简单的方法就是改变bean的作用于,把’singleton’改为’prototype’,这样每次请求bean就相当于new Bean(),这样就可以保证线程安全了。...
  • java 单例模式线程安全问题SpringIOC容器默认提供bean的访问作用域是单例模式。即在整个application生命周期中,只有一个instance。因此在多线程并发下,会有线程安全风险。我们在MVC框架下的servlet就是线程安全的...
  • 由于Spring容器生成的Bean都是默认单例的,故service会实例化一个单例对象。 多线程环境修改类变量,由于类变量存储位置属于方法区,由多线程共享,各线程对该共享存储区域的操作可能相互影响,产生如线程A对类变量...
  • singleton:单例模式,Spring IoC容器中只会存在一个共享的Bean实例,无论有多少个Bean引用它,始终指向同一对象。Singleton作用域是Spring中的缺省作用域,也可以显示的将Bean定义为singleton模式,配置为: &...
  • When the thread requests the singleton bean, it is going to refer (with help of reference variable in the stack) to the bytecode of singleton bean in heap. So, multiple threads can refer to the ...
  • Spring容器本身没有提供Bean线程安全策略,因此Spring容器中的Bean本身不具备线程安全的特性。 单例和原型是spring bean的两个作用域。 对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然...
  • 1、在@Controller/@Service等容器中,默认情况下,scope值是单例-singleton的,也是线程安全的。 2、尽量不要在@Controller/@Service等容器中定义...5.如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Be
  • 关于单例bean线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean线程安全的。如果你的bean有多种状态的话...
  • 如果该逻辑涉及到对该单例状态(成员变量)的改变,就会有线程安全的问题。 Spring使用ThreadLocal来解决线程安全问题,每个线程去执行业务代码的时候,都会去内存申请临时变量,这样就不会涉及变量并发访问冲突的问
  • Springmvc controller 是单例线程安全的 当时有这么一个面试题 随手记录一下了; spring的controller默认是单例的。 spring的bean作用域有5个,如下: singleton:单例模式,当spring创建applicationContext容器...
  • 1、有状态的bean与无状态的bean 有状态bean:每个用户有自己特有的一个实例,在用户的生存期内,bean保存了用户的信息,即有状态;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会...
  • Spring 的 bean默认是单例的,在高并发下,如果在 ...本文就对单例 bean 及多线程安全的问题做一次较为深入的探讨,也是对自我的一次反省,之后的开发中,杜绝此类问题,修正开发习惯。单例模式首先我们回顾一下单例...
  • Spring容器中的Bean是否线程安全,容器本身并没有提供Bean线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。Spring 的 bean 作用域(scope)类型1、...
  • 二、spring的单例模式与线程安全 1.spring框架里的bean获取实例的时候都是默认单例模式,所以在多线程开发里就有可能会出现线程不安全的问题。当多个用户同时请求一个服务器时,容器(tomcat)会给每一个请求分配一...
  • 一、Spring单例模式与线程安全 Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式,这是在多线程开发的时候要尤其注意的地方。 单例模式的意思就是只有一个实例。单例模式确保某一个类只有一...
  • 本质上,正在发生的事情是在这些线程的每个线程上执行生成的字节码,该字节码代表您创建的单例对象。 现在,如何发生这种情况的内幕如下: 每个JVM线程都有一个专用JVM堆栈,与该线程同时创建。现在,JVM具有一个在...
  • 1.controller/model案例:https://www.jianshu.com/p/ce9415465ee42.netty + springbootnetty channel的线程安全性与@Sharable1. Bootstrap中直接添加sb.childHandler(new MySimpleChannelInboundHandler())这个...
  • 点击关注公众号,实用技术文章及时了解来源:blog.csdn.net/fuzhongmin05/article/details/1008498671、有状态的bean与无状态的bean有状...
  • 目录单例(singleton)作用域每个添加@RestController或@Controller的控制器,默认是单例(singleton),这也是Spring Bean的默认作用域。GreetingController.java代码如下:package com.example.controller;import java...
  • Spring容器本身没有提供Bean线程安全策略,因此,也可以说Spring容器中的bean不是线程安全的。 ​ 如何处理线程安全问题,分情况讨论: ​ Spring的作用域(scope): singleton:单例,默认作用域。 prototype:...
  • spring 容器中的 bean 是否线程安全,容器本身并没有提供 bean线程安全策略,因此可以说 spring 容器中的 bean 本身不具备线程安全的特性,但是具体还是要结合具体 scope 作用域的去研究 spring 的 bean 作用域 ...
  • spring中的bean线程安全的吗 参考网址 https://mp.weixin.qq.com/s/oPSbXOJTi0Du9mkI517ydA https://mp.weixin.qq.com/s/7yCaQ1ek5HD-C13SSWEKfw 证明controller是单例的 示例代码1 新建springboot工程,引入web...
  • org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'session' is not active for the current thread; consider defining a ...
  • (You have to manage it by code) If you want to make a Bean thread safe create your bean classes stateless. Stateless Beans : Stateless session beans are session beans whose instances have no ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 33,603
精华内容 13,441
关键字:

单例bean线程安全