java单例多例的线程安全问题

我的理解:
成员变量是保存在堆中,所有线程都能访问;局部变量是保存在每条线程的各自的栈中,互不影响。
单例模式下成员变量会有线程安全问题,因为每条线程都可能会访问到该成员变量;多例模式下则不会有这问题,不会有这问题的原因是 栈中的对象分别指向堆中属于自己的成员变量。
我的理解是否有问题?或者有什么补充的。
谢谢!

因为spring是默认是单例模式,而单例模式有三种常用的实现形式都是静态化实例对象。既然静态化了,那么就属于静态区,被多线程共享(每个线程在虚拟机栈里都有自己的栈空间),所以线程不安全。但是如果自己new一个对象是属于线程内部的局部变量,存在new他的那个线程的一个栈帧里,就是线程安全的。所以总结起来就是,这两种的对象存在不一样的地方,共享范围也不一样,所以有的线程安全,有的不安全,就会出现你说的那种情况,望采纳,谢谢

线程安全的原因在于并发操作同一个对象无法保证操作的原子性、事务性和一致性。

“多例模式下则不会有这问题,不会有这问题的原因是 栈中的对象分别指向堆中属于自己的成员变量。”

我不知道你说的多例模式是什么模式,也许你想表达的是多个对象实例的情况,这种情况一样可能有线程安全的问题,体现在多个实例访问类的静态成员,多个线程访问同一个实例。
虽然变量是在堆栈上定义的,但是指向的对象还是在堆上的,因此通过某些方式它们还是会被别的线程访问到。比如说借助参数、全局变量、装入了arraylist等。

不管单例还是多例,只要多个线程能够**访问并改变****同一个对象实例**,都可以说成在线程安全。

读了几遍你写的,还是读出了毛病。。。
其实可以这么理解的,单例是多个线程访问 1个资源,肯定有线程安全问题,
多例虽然有多个对象(比如说有2个吧),但是访问的线程如果有10个,其中5个访问资源1,这就相当于5个线程访问同一个资源,也就是转化成上面
单例线程不安全的原因,所以单例还是多例只要是多线程都不安全,
你说错的原因是,虽然局部变量是保存在各自的栈中,但是多例中的每个例,都是全局变量,保存在堆中,,差点把我绕进去

使用单例模式简单来说生成对象时属性都一样,即你new一百次,通过方法得到的结果都一样(比如获取静态资源文件,工具类等). 所以就没必要生成多个对象浪费服务器内存,他和静态类又不同,因为单例本质也是对象系统,长期不使用,也会给cg清除.但是静态类不同,静态类的成员变量和有静态方法会在程序的整个生命周期存在,比如在服务器内在中加载后服务器不关,就会一直存在,同理的有servlet的ServletContext对象和jsp的application对象

才疏学浅 你可以参考一下这个http://www.mamicode.com/info-detail-1728587.html

线程安全问题是多线程访问共享数据才会存在的,这包括两种情况,一是多线程访问单例的成员变量,二是多线程访问静态变量(数据库这里这就不算了)
线程内部不会产生安全问题,从java内存模型来看,一个线程对于对象的操作流程应该是,访问主存中的对象并复制到工作内存当中,在工作内存中对副本做相应操作,操作完成后再写入主存,线程见会产生线程安全问题,正是因为一个线程在操作工作内存中的副本时,在写入主存之前,其他线程是不可见的,所以会产生问题,而线程内部,无论哪个方法怎么执行,对副本都是可见的,你说的两种情况其实也是不存在的,情况1,如果你在一个线程中,一个方法执行到一半,去执行另一个方法,代码怎么写?肯定是在第一个方法中调用第二个方法,那么第二个方法执行完,第一个方法的后半部分肯定要接着第二个的逻辑写,还接着第一个方法的前一半写,那就是代码写错了,情况2相同

楼主理解没问题,但既然是单例,为什么还要改变成员变量呢?

楼上的大佬们其实都说的很对,但是过于学术化不利于新人理解。
其实可以想简单点,java的对象就是一个独立的个体,比如说一个人,这一个人去做多件事当然是资源共享的,比如一张嘴不可能同时说次话。
多例与单例比只是多了几个人(对象),但是这其中任何一个人也不可能同时说两次话,也就是说同时有两个线程持有一个对象就是不安全的,
所谓的多例只是生产人(对象)的模式不同而且,与线程安全无关。
只是单例必然发生线程安全问题,才会常常与单例一起讨论。

个人理解,仅参考。

真的把我绕晕了,给你解决一下这个问题。

下面只针对**引用类型**来说一下多线程的安全问题,不包括基本类型:

  1. java里面每一个线程都是对应一个栈,线程里面运行的方法对应一个栈帧,简单来说,线程A和线程B都需要运行方法X和方法Y,但线程A运行这两个方法是在A栈里面,线程B运行这两个方法是在B栈里面。 你可以这么理解,线程A运行的方法X的一个副本,线程B运行的也是方法X的副本,这两个副本什么关系都没有。 方法内部的'局部变量'的也就属于当前这个方法的副本,其他副本都拿不到,也就相当于其他线程都拿不到,所以局部变量是线程安全的,不需要考虑线程同步问题。
  2. 但是对于全局变量来说,这个类的实例一经创建,这个实例对象的变量就可以被所有的线程共享,可以同时修改,会存在线程同步问题,也就是你上面所说的 “如果是A线程在执行改变了成员变量的值还未同步到主存,而此时B线程开始执行,这时B线程拿到的该成员变量是最早的吗?”, 这个值是指全局变量。
  3. 对于你提的单例和多例来说,我们还是要归根到变量的类型上面,无论是单例还是多例,只要是方法内部的局部变量,都是线程安全地,你可以放心的用。 但如果是全局变量,对于单例来说,这个单例类只有一个实例对象,所有的线程都访问这一个对象,也就是都是访问的同一个对象的同一个全局变量,这时候是有线程同步问题的;对于多例来说,多例类可以创建多个对象,如果创建了两个对象A1,A2,那么这个全局变量在内存中是两份,一份属于A1,一份属于A2,两个线程分别修改A1的变量,A2的变量,修改的是两个副本,所以不存在同步问题;但是如果两个线程都访问对象A1,这时候两个线程范访问的就是同一个全局变量了,就存在线程安全问题。

总之,判断一个对象是否存在线程安全问题,就是看这个对象的全局变量是不是可以同时被多个线程访问修改。如果一个类没有全局变量,只有一大堆的方法和不能修改的常量,那你来多少线程访问都没事(这其实就是工具类)。

至于题主说的单例和多例,不能从类的角度分析线程是否安全,是要归到“对象”这个层面上进行分析。