精华内容
下载资源
问答
  • 一、Spring单例模式与线程安全   Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式,这是在多线程开发的时候要尤其注意的地方。   单例模式的意思就是只有一个实例。单例...

    一、Spring单例模式与线程安全

     

    Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式,这是在多线程开发的时候要尤其注意的地方。

     

    单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
    当多用户同时请求一个服务时,容器会给每一个请求分配一个线程,这是多个线程会并发执行该请求多对应的业务逻辑(成员方法),此时就要注意了,如果该处理逻辑中有对该单列状态的修改(体现为该单列的成员属性),则必须考虑线程同步问题
    同步机制的比较  ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。 
      在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。 
      而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。 
      由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK 5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用
     概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。 
      Spring使用ThreadLocal解决线程安全问题 
      我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。 
      一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程
    ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。 
    如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。 或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。   线程安全问题都是由全局变量及静态变量引起的。  
    若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
    1) 常量始终是线程安全的,因为只存在读操作。 
    2)每次调用方法前都新建一个实例是线程安全的,因为不会访问共享的资源。
    3)局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。
    有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象  ,可以保存数据,是非线程安全的。在不同方法调用间不保留任何状态。
    无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象  .不能保存数据,是不变类,是线程安全的。
    有状态对象:
    无状态的Bean适合用不变模式,技术就是单例模式,这样可以共享实例,提高性能。有状态的Bean,多线程环境下不安全,那么适合用Prototype原型模式。Prototype: 每次对bean的请求都会创建一个新的bean实例。
    Struts2默认的实现是Prototype模式。也就是每个请求都新生成一个Action实例,所以不存在线程安全问题。需要注意的是,如果由Spring管理action的生命周期, scope要配成prototype作用域。

     

    二、线程安全案例:

    SimpleDateFormat(下面简称sdf)类内部有一个Calendar对象引用,它用来储存和这个sdf相关的日期信息,例如sdf.parse(dateStr), sdf.format(date) 诸如此类的方法参数传入的日期相关String, Date等等, 都是交友Calendar引用来储存的.这样就会导致一个问题,如果你的sdf是个static的, 那么多个thread 之间就会共享这个sdf, 同时也是共享这个Calendar引用, 并且, 观察 sdf.parse() 方法,你会发现有如下的调用:
    Date parse() {
      calendar.clear(); // 清理calendar
      ... // 执行一些操作, 设置 calendar 的日期什么的
      calendar.getTime(); // 获取calendar的时间
    }
    这里会导致的问题就是, 如果 线程A 调用了 sdf.parse(), 并且进行了 calendar.clear()后还未执行calendar.getTime()的时候,线程B又调用了sdf.parse(), 这时候线程B也执行了sdf.clear()方法, 这样就导致线程A的的calendar数据被清空了(实际上A,B的同时被清空了). 又或者当 A 执行了calendar.clear() 后被挂起, 这时候B 开始调用sdf.parse()并顺利i结束, 这样 A 的 calendar内存储的的date 变成了后来B设置的calendar的date
    这个问题背后隐藏着一个更为重要的问题--无状态:无状态方法的好处之一,就是它在各种环境下,都可以安全的调用。衡量一个方法是否是有状态的,就看它是否改动了其它的东西,比如全局变量,比如实例的字段。format方法在运行过程中改动了SimpleDateFormat的calendar字段,所以,它是有状态的。
      这也同时提醒我们在开发和设计系统的时候注意下一下三点:
      1.自己写公用类的时候,要对多线程调用情况下的后果在注释里进行明确说明
      2.对线程环境下,对每一个共享的可变变量都要注意其线程安全性
      3.我们的类和方法在做设计的时候,要尽量设计成无状态的
     三.解决办法
      1.需要的时候创建新实例:
      说明:在需要用到SimpleDateFormat 的地方新建一个实例,不管什么时候,将有线程安全问题的对象由共享变为局部私有都能避免多线程问题,不过也加重了创建对象的负担。在一般情况下,这样其实对性能影响比不是很明显的。
      2.使用同步:同步SimpleDateFormat对象
    public class DateSyncUtil {
        private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
          
        public static String formatDate(Date date)throws ParseException{
            synchronized(sdf){
                return sdf.format(date);
            }  
        }
        
        public static Date parse(String strDate) throws ParseException{
            synchronized(sdf){
                return sdf.parse(strDate);
            }
        } 
    }
      说明:当线程较多时,当一个线程调用该方法时,其他想要调用此方法的线程就要block,多线程并发量大的时候会对性能有一定的影响。
      3.使用ThreadLocal: 
    public class ConcurrentDateUtil {
        private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
            @Override
            protected DateFormat initialValue() {
                return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            }
        };
        public static Date parse(String dateStr) throws ParseException {
            return threadLocal.get().parse(dateStr);
        }
        public static String format(Date date) {
            return threadLocal.get().format(date);
        }
    }
    public class ThreadLocalDateUtil {
        private static final String date_format = "yyyy-MM-dd HH:mm:ss";
        private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>(); 
     
        public static DateFormat getDateFormat()   
        {  
            DateFormat df = threadLocal.get();  
            if(df==null){  
                df = new SimpleDateFormat(date_format);  
                threadLocal.set(df);  
            }  
            return df;  
        }  
        public static String formatDate(Date date) throws ParseException {
            return getDateFormat().format(date);
        }
        public static Date parse(String strDate) throws ParseException {
            return getDateFormat().parse(strDate);
        }   
    }
      说明:使用ThreadLocal, 也是将共享变量变为独享,线程独享肯定能比方法独享在并发环境中能减少不少创建对象的开销。如果对性能要求比较高的情况下,一般推荐使用这种方法。
      4.抛弃JDK,使用其他类库中的时间格式化类:
      1.使用Apache commons 里的FastDateFormat,宣称是既快又线程安全的SimpleDateFormat, 可惜它只能对日期进行format, 不能对日期串进行解析。
      2.使用Joda-Time类库来处理时间相关问题
      做一个简单的压力测试,方法一最慢,方法三最快,但是就算是最慢的方法一性能也不差,一般系统方法一和方法二就可以满足,所以说在这个点很难成为你系统的瓶颈所在。从简单的角度来说,建议使用方法一或者方法二,如果在必要的时候,追求那么一点性能提升的话,可以考虑用方法三,用ThreadLocal做缓存。
      Joda-Time类库对时间处理方式比较完美,建议使用。

    展开全文
  • Spring解决单例bean线程安全问题

    千次阅读 多人点赞 2019-05-27 17:32:11
    首先我们应该知道线程安全问题一般发生在成员变量上,这是为什么啦? 因为成员变量是存放在堆内存中...下面我们就来研究下如何解决Spring中单例Bean线程安全问题 @RestController //@Scope("prototype") publ...

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

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

    因为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模式去启动,打断点。不需要执行完程序,然后再次调用该接口。或者多次调用该接口,便会出现以下控制台所示的结果。

     

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

    解决方式一:

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

     

     

    解决方案二  ThreadLocal解决问题

    @RestController
    //@Scope("prototype")
    public class BeanController {
    	private static ThreadLocal<Integer> content = new ThreadLocal<Integer>() {
            @Override
            protected Integer initialValue() {
                return (int)(Math.random()*10+100);
            }
        };
        private static ThreadLocal<String> test = new ThreadLocal<String>() {
            @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")类似。

     

     

    展开全文
  • Spring中的bean线程是否安全 Spring容器中的Bean大致分为5个范围 singleton:默认(大多数的默认),每个容器中只有一个Bean实例 prototype:每一个bean请求提供一个实例 request:为每一个网络请求创建一个实例...

    Spring中的bean线程是否安全

    • Spring容器中的Bean大致分为5个范围

      • singleton默认(大多数的默认),每个容器中只有一个Bean实例
      • prototype:每一个bean请求提供一个实例
      • request:为每一个网络请求创建一个实例,在请求完成之后,bean会失效并被垃圾回收(几乎不用)。
      • session:每个session中有一个bean实例,session过期,bean失效(几乎不用)。
    • 答案是否定的spring bean默认是singleton,线程不安全,java web系统,在spring bean中不放实例变量,以至于被多个线程访问到,进行修改,导致线程安全问题,一般都是组件互相调用,然后最终多个线程访问数据库不会导致多个线程并发访问内存里的共享变量
      在这里插入图片描述

    展开全文
  • spring 中的 bean线程安全的吗?

    万次阅读 2019-07-16 14:55:53
    spring 中的 bean线程安全的吗? Spring 不保证 bean线程安全。 默认 spring 容器中的 bean 是单例的。当单例中存在竞态条件,即有线程安全问题。如下面的例子 计数类 package constxiong.interview....

    spring 中的 bean 是线程安全的吗?

     Spring 不保证 bean 的线程安全。
    默认 spring 容器中的 bean 是单例的。当单例中存在竞态条件,即有线程安全问题。如下面的例子

    计数类

    package constxiong.interview.threadsafe;
    
    /**
     * 计数类
     * @author ConstXiong
     * @date 2019-07-16 14:35:40
     */
    public class Counter {
    
    	private int count = 0;
    	
    	public void addAndPrint() {
    		try {
    			Thread.sleep(10);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		System.out.println(++count);
    	}
    	
    }
    

     

    spring 配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xsi:schemaLocation="
    		http://www.springframework.org/schema/beans 
    		http://www.springframework.org/schema/beans/spring-beans.xsd
    	    http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
            
    	<bean id="counter" class="constxiong.interview.threadsafe.Counter" />
    	
    </beans>

     

    测试类

    package constxiong.interview.threadsafe;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class CounterTest {
    	
    	public static void main(String[] args) {
    		final ApplicationContext context = new ClassPathXmlApplicationContext("spring_safe.xml");
    
    		for (int i = 0; i < 10; i++) {
    			new Thread(){
    				@Override
    				public void run() {
    					Counter counter = (Counter)context.getBean("counter");
    					for (int j = 0; j < 1000; j++) {
    						counter.addAndPrint();
    					}
    				}
    			}.start();
    		}
    		
    	}
    	
    }
    

    打印结果开头和结尾

    1
    5
    7
    4
    2
    6
    3
    8
    9
    .
    .
    .
    9818
    9819
    9820
    9821
    9822
    9823
    9824
    9825

    期望打印出的最大值应该是 10000

     

    修改 spring 配置文件,把 bean 的作用域改为 prototype

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xsi:schemaLocation="
    		http://www.springframework.org/schema/beans 
    		http://www.springframework.org/schema/beans/spring-beans.xsd
    	    http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
            
    	<bean id="counter" class="constxiong.interview.threadsafe.Counter" scope="prototype"/>
    	
    </beans>

    测试结果输出10个 1000

    即每个线程都创建了一个 Counter 对象,线程内独自计数,不存在线程安全问题。但是不是我们想要的结果,打印出 10000。

     

    所以 spring 管理的 bean 的线程安全跟 bean 的创建作用域和 bean 所在的使用环境是否存在竞态条件有关,spring 并不能保证 bean 的线程安全。

     


    【Java面试题与答案】整理推荐

     

    展开全文
  • Spring单例bean线程安全问题学习研究问题引入问题的回答如何解决?背景知识Spring 的 bean 作用域(scope)类型原型Bean与单例Bean理论依据实验过程多线程模型ThreadLocal@Scope("prototype")@Scope("request")...
  • Spring中的Bean线程安全的吗?

    万次阅读 多人点赞 2019-03-12 23:59:43
    Spring容器中的Bean是否线程安全,容器本身并没有提供Bean线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。 Spring 的 bean 作用域(scope)类型...
  • Spring中的单例bean线程安全问题 因为成员变量是存放在堆内存中,而堆内存又是线程共享的,这就造成了线程安全问题 因为Spring中的Bean默认是单例的,所以在定义成员变量时也有可能会发生线程安全问题 单例的bean ...
  • Spring Bean线程安全

    2017-05-07 11:06:59
    前两天在使用spring Bean的时候,发现跑出来的数据总是存在着一些奇怪的值,这些值在当前的处理当中本该不会出现,找了半天,发现时上一个线程使用该Bean后的遗留的值。后面查看了spring Bean的相关知识后,找到了...
  • Spring单例bean线程安全

    千次阅读 2018-03-29 17:04:43
    Spring容器中的bean默认是单例模式。当多个客户端同时请求一个服务时,容器会给每一个请求分配一个线程... Spring使用ThreadLocal解决线程安全问题。一般情况下,只有无状态的Bean才可以在多线程环境下共享。Sprin...
  • Spring容器中的Bean是否线程安全,容器本身并没有提供Bean线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。 Spring 的 bean 作用域(scope)...
  • 浅谈Spring中的BeanBean线程安全的吗?
  • 如何保证spring bean中的线程安全

    千次阅读 2016-01-30 10:23:01
    写在前面:这两天做了一个查询接口,由于查询条件和参数太多,采用了链式操作,链式操作的类是个...最终通过检索关键字“spring bean 线程安全”找到了答案: [url]http://www.cnblogs.com/doit8791/p/4093808.ht...
  • Spring中的Bean是否线程安全和Spring无关。因为Spring只承担了创建和管理Bean的职责,并没有对Bean进行任何修改。 因为Ioc并不具备线程安全的特性,但是要看@Scope, 原型模式一定是线程安全的,因为每次都需要创建...
  • spring bean线程安全的吗? 思路:1、spring bean的一个作用域,比如singleton,protopyte等 在这个Spring中的Bean默认是单例模式的,框架并没有对bean进行多线程的封装处理 实际上大部分时间Bean是无状态的...
  • Spring中单例Bean线程安全的吗

    万次阅读 多人点赞 2019-09-02 17:16:15
    Spring容器中的Bean是否线程安全,容器本身并没有提供Bean线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。 Spring 的 bean 作用域(scope)...
  • 容器本身并没有提供Bean线程安全策略,谈论Bean是否安全首先看一下它的作用范围: Spring 的 bean 作用域(scope)类型  1、singleton:单例,默认作用域。  2、prototype:原型,每次创建一个新对象。  3、...
  • Spring框架中的单例Bean默认是单例模式,不是线程安全的。Spring框架并没有对单例Bean进行多线程的封装处理。 关于线程是否安全,可以从Bean的状态来考虑是否要进行处理,有状态的Bean就是有数据存储功能,例如VO...
  • spring中bean本身不存在线程安全问题,要看具体使用情况, 当bean的作用域是prototype时,每次获取bean都是创建一个新的实例,因此不存在线程安全问题。 当bean的作用域是singleton时,不同线程访问同一个bean,如果...
  • Spring容器中的Bean是否线程安全,容器本身并没有提供Bean线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但还是要结合具体的Bean的Scope(作用域)来分析。 首先我们先来了解Bean的作用...
  • Spring的单例Bean线程安全问题

    千次阅读 2019-10-22 15:57:32
    线程安全问题都是由成员变量及静态变量引起的。 若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的; 若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能...
  • Spring的bean默认scope属性为singleTon,即单例的,有线程安全问题 如果设置@Scope(“prototype”),则每次都会创建新对象,不存在线程安全问题 public static void main(String[] args) { ...
  • Bean的作用域  Spring 3中为Bean定义了5中作用域,分别为singleton(单例)、prototype(原型)、request、session和global session,5种作用域说明如下: singleton:单例模式,Spring IoC容器中只会存在一个...
  • Spring的bean线程安全的吗? 大家可以回顾下线程不安全构成的三要素: 1,多线程环境 2,访问同一个资源 3,资源具有状态性 那么Spring的bean模式是单例,而且后端的程序,天然就处于一个多线程的工作环境。 那么...
  • 事实上spring中bean线程安全性与spring是无关的,是和写的代码有关系! Spring的单例bean在多线程下是存在线程安全性问题的,主要是因为当多个线程同时操作同一个对象的时候,对这个对象的非静态成员变量的写操作...
  • spring中bean线程安全问题

    千次阅读 2019-05-06 10:24:43
    Spring容器生成的Bean都是默认单例的,那么spring是怎么处理并发的情况下线程安全问题呢? 在讲单例和线程安全之前首先我们要明白一点,那就是在单例模式下多线程也可以同时访问同一个对象。单例模式顾名思义,所有...
  • Spring容器中的Bean是否线程安全,容器本身并没有提供Bean线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。 Spring 的 bean 作用域(scope)...
  • 我们知道Spring相对于之前框架的明显一点区别就是Spring容器生成的Bean都是默认单例的,初读到这里的时候我也是有点疑惑,所以写这篇文章来谈谈线程安全和单例多例的问题。 在讲单例和线程安全之前首先我们要明白...
  • Spring容器中的Bean是否线程安全,容器本身并没有提供Bean线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。 Spring 的 bean 作用域(scope)...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 68,118
精华内容 27,247
关键字:

bean的线程安全