1. java单例模式饿汉式会有线程安全问题吗
1、答案:不会有线程安全问题。
2、首先,一个类可以被使用必须经过 加载、连接和初始化
3、饿汉模式的对象创建是在初始化的时候创建的,初始化操作是jvm执行的(会给类变量赋初始值,执行静态代码块等,类变量是用static修饰的变量),并且在我们看来一个类的初始化只会执行一次(jvm会控制),是不会有线程安全问题出现的
3、纠错:不是“类一加载就实例化”,而是类先加载、连接(此步骤执行时间不固定,但是在初始化执行前必须执行结束)。初始化执行的时机是你new了一个对象,或者使用了反射机制,或者调用类的静态变量和方法或者启动有main方法的类
2. 单例模式的作用及创建方法
单例模式作为常见的设计模式之一,在java的项目开发中会时常的用到。Java Singleton模式即保证在JVM运行时,一个类Class只有一个实例存在。
单例模式有什么好处呢?
最简单的一个例子就是网站计数器的设计了。当我们想要统计当前网站的在线人数时,一个显而易见的问题就是并发所带来的线程安全问题,当我们对这个计数器(网站人数)在同一时刻进行操作,再保存计数时就会造成数据的混乱,后者覆盖前者的结果。一种解决方案就是把这个计数器设置为唯一对象,所有人都必须共用同一份数据。
实现唯一对象最好的解决办法就是让类自己负责保存它的唯一实例,并且让这个类保证不会产生第二个实例,同时提供一个让外部对象访问该实例的方法。自己的事情自己办,而不是由别人代办,这非常符合面向对象的封装原则。
单例模式的三个特点:
只有在自身需要的时候才会行动,从来不知道及早做好准备。它在需要对象的时候,才判断是否已有对象,如果没有就立即创建一个对象,然后返回,如果已有对象就不再创建,立即返回。
该方法在多线程情况下有可能重复创建实例,以下是线程安全的懒汉模式
这种模式的缺点是加锁造成了效率下降,并且在绝大部分情况下是不需要同步的。使用双重检验锁(DCL),只在第一次初始化的时候进行同步加锁
该方式在类加载的时候就被实例化了。
这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟饿汉不同的是(很细微的差别):饿汉方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显式通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。
想象一下,如果实例化instance很消耗资源,我想让它延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式就显得很合理。
关于类加载情况下单例模式,如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类 装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。修复的办法是
3. 设计模式之单例模式
单例设计模式理解起来非常简单。一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫单例模式。
下面的示例中如果每个类都创建一个 Logger 实例,就可能造成日志内容被覆盖的情况。
如果有些数据在系统中只应保存一份,那就比较适合设计为单例类。比如悄伍,配置信息类,全局 ID 生成器等。
要实现一个单例,我们要考虑以下几点:
懒汉式相对于饿汉式的优势是 “支持延迟加载” 。但缺点也很明显,因为使用了 synchronized 关键字导致这个方法的 “并发度很低” 。如果这个单例类偶尔会被用到,那这种实现方式还可以接受。但是,如果频繁地用到,就会导致性能瓶颈,这种实现方式就不可取了。
这是一种既支持延迟加载、又支持高并发的单例实现方式。
在 java1.5 以下 instance = new Singleton(); 有指令重排问题,需要给 instance 成员变量加上 volatile 关键字,java1.5 之后不会再这个问题。
这种方式利用了 Java 的静态内渣知部类,有点类似饿汉式,但又能做到了延迟加载。
当外部类 Singleton 被加载的时候,并不会创建 SingletonHolder 实例对象。只有当调用 getInstance() 方法时,SingletonHolder 才会被加载,这个时候才会创建 instance。insance 的唯一性、创建过程的线程安全性,都由 JVM 来保证。所以,这种实现方法既保证了线程安全,又能做到延迟加载。
这是一种最简单的实现方式,基于枚举类型的单例实现。这种实现方式是通过 Java 枚举类型本身的特性,保证了实例创建的线程安全性和实例的唯一性。
上面的单例类对象是进程唯一的,一个进程只能有一个单例对象。那如何实现一个线程唯一的单例呢?
假设 IdGenerator 是一个线程唯一的单例类。在线程 A 内,我们可以创建一启梁或个单例对象 a。因为线程内唯一,在线程 A 内就不能再创建新的 IdGenerator 对象了,而线程间可以不唯一,所以,在另外一个线程 B 内,我们还可以重新创建一个新的单例对象 b。
我们通过一个 ConcurrentHashMap 来存储对象,其中 key 是线程 ID,value 是对象。这样我们就可以做到,不同的线程对应不同的对象,同一个线程只能对应一个对象。实际上,Java 语言本身提供了 ThreadLocal 工具类,可以更加轻松地实现线程唯一单例。
4. Java涓23绉嶈捐℃ā寮忊斺斿崟渚嬫ā寮
Java涓栫晫涓镄勫崟渚嫔湥娈匡细23绉嶈捐℃ā寮忔帰绱
鍦↗ava缂栫▼涓锛屽崟渚嬫ā寮忓傚悓涓搴фˉ姊侊纴杩炴帴镌瀵硅薄镄勭敓锻藉懆链熺$悊鍜屽叏灞璁块梾镄勭簿濡椤钩琛°傝╂垜浠涓璧锋繁鍏ユ帰璁ㄥ叚绉嶅父瑙佷笖钖勬湁鐗硅壊镄勫疄鐜版柟寮忥纴瀹冧滑鍒嗗埆鏄锛氭噿姹夊纺銆侀タ姹夊纺銆佸弻閲嶆镆ラ挛瀹氥侀润镐佸唴閮ㄧ被銆佹灇涓炬硶涓嶵hreadLocal锛屼互鍙娄竴绉嶅垱鏂扮殑娉ㄥ唽寮忓崟渚嬫ā寮忋
娉ㄥ唽寮忓崟渚嬬殑闱╂柊锛氶噰鐢ㄩ润镐丮ap绠$悊鍗曚緥瀵硅薄锛屽綋闇瑕佹椂锛屽疄渚嫔寲骞惰繑锲炪傚湪璇稿傜嚎绋嬫睁銆佹暟鎹搴撹繛鎺ユ睁銆佹棩蹇楀硅薄鍜岄厤缃鏂囦欢绛夊満鏅涓锛屽畠鑳戒缭璇侀珮鏁堜笖绾跨▼瀹夊叏镄勮块梾銆
铹惰岋纴鍗曚緥妯″纺骞堕潪涓甯嗛庨‘锛屽畠涔熼溃涓存写鎴桡细绾跨▼瀹夊叏闂棰橀渶瑕佸阀濡椤湴浣跨敤阌佹満鍒讹纴搴忓垪鍖栨椂鍙鑳戒骇鐢熷氢釜瀹炰緥锛岃繖镞秗eadResolve()鏂规硶灏辨淳涓婄敤鍦轰简锛涘弽灏勫彲鑳藉艰嚧绉佹湁鏋勯犲嚱鏁拌缁曡繃锛岃繖镞舵垜浠闇鎶涘嚭寮傚父浠ョず璀﹀憡銆
缁撹锛氩崟渚嬫ā寮忔槸鎺у埗瀵硅薄鐢熷懡锻ㄦ湡鍜屽疄鐜板叡浜璧勬簮镄勫叧阌銆傚湪阃夋嫨鍝绉嶅疄鐜版柟寮忔椂锛屽姟蹇呮潈琛$嚎绋嫔畨鍏ㄣ佸簭鍒楀寲鍜屽弽灏勭殑钥冮噺锛屼互阆垮厤浠g爜澶嶆潅镐у崌绾у拰镐ц兘镄勯殣镐ф崯钥椼傚彧链夊湪鎭板綋镄勬椂链猴纴瀹冩墠鑳芥垚涓轰唬镰佽捐′腑镄勫缑锷涘姪镓嬨