单例模式
优点
只生成一个实例,减少其他开销 该实例全局访问,优化共享资源
1.饿汉单例模式
代码示例
public class SingletonDemo_1 {
private SingletonDemo_1 ( ) {
}
private static SingletonDemo_1 instance = new SingletonDemo_1 ( ) ;
public static SingletonDemo_1 getInstance ( ) {
return instance;
}
}
class SingletonDemo01Test {
public static void main ( String[ ] args) {
SingletonDemo_1 instance = SingletonDemo_1. getInstance ( ) ;
SingletonDemo_1 instance2 = SingletonDemo_1. getInstance ( ) ;
System. out. println ( instance== instance2) ;
}
}
类被加载到内存时自动生成一个该类实例,通过方法获得同一个该实例对象 不适用于需要开辟大量空间,非即时使用的场景
2.懒汉式单例模式
package com. niss. singleton;
public class SingletonDemo_2 {
private SingletonDemo_2 ( ) {
}
private static SingletonDemo_2 instance;
public synchronized static SingletonDemo_2 getInstance ( ) {
if ( instance == null) {
instance = new SingletonDemo_2 ( ) ;
}
return instance;
}
}
class SingletonDemo02Test {
public static void main ( String[ ] args) {
SingletonDemo_2 instance = SingletonDemo_2. getInstance ( ) ;
SingletonDemo_2 instance2 = SingletonDemo_2. getInstance ( ) ;
System. out. println ( instance== instance2) ;
}
}
类初始的时候,不立即加载该对象 提供获取该对象的方法,有synchronized,效率低
3.DLC懒汉式单例模式
由于JVM底层内部模型问题,偶尔出现问题,不建议使用
package com. niss. singleton;
public class SingletonDemo_3 {
private SingletonDemo_3 ( ) {
}
private volatile static SingletonDemo_3 instance;
public static SingletonDemo_3 getInstance ( ) {
if ( instance == null) {
synchronized ( SingletonDemo_3. class ) {
if ( instance== null) {
instance = new SingletonDemo_3 ( ) ;
}
}
}
return instance;
}
}
class SingletonDemo03Test {
public static void main ( String[ ] args) {
SingletonDemo_3 instance = SingletonDemo_3. getInstance ( ) ;
SingletonDemo_3 instance2 = SingletonDemo_3. getInstance ( ) ;
System. out. println ( instance == instance2) ;
}
}
将synchronized关键字调整到方法内部,避免各个线程必须排队等待,一个一个访问获取该实例,改进效率低的缺点 但是因为synchronized关键字设置在内部,可能会出现多个线程同时进入该方法中因为其中一个线程进入步骤B时,另一个已经进入步骤A,从而判断instance不为null,直接使用该类的instance,引发一些意外的问题,所以加入volatile关键字保证该实例的修改对所有线程的可见性
4.静态内部类实现单例模式(饿汉式改进)
package com. niss. singleton;
public class SingletonDemo_4 {
private SingletonDemo_4 ( ) {
}
private static class InnerClass {
private static final SingletonDemo_4 instance = new SingletonDemo_4 ( ) ;
}
public static SingletonDemo_4 getInstance ( ) {
return InnerClass. instance;
}
}
class SingletonDemo04Test {
public static void main ( String[ ] args) {
SingletonDemo_4 instance = SingletonDemo_4. getInstance ( ) ;
SingletonDemo_4 instance2 = SingletonDemo_4. getInstance ( ) ;
System. out. println ( instance == instance2) ;
}
}
通过设置内部类来存放该实例,只有线程调用方法时,才会将内部类调用到内存,才会产生该实例,实现懒加载 线程安全。调用效率高
以上单例模式均会被反射机制给破坏,测试代码如下
class SingletonDemo04Test02 {
public static void main ( String[ ] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
SingletonDemo_4 instance = SingletonDemo_4. getInstance ( ) ;
Constructor< SingletonDemo_4> constructor
= SingletonDemo_4. class . getDeclaredConstructor ( null) ;
constructor. setAccessible ( true ) ;
SingletonDemo_4 instance2 = constructor. newInstance ( null) ;
System. out. println ( instance== instance2) ;
System. out. println ( instance. hashCode ( ) ) ;
System. out. println ( instance2. hashCode ( ) ) ;
}
}
通过Java反射机制来通过构造器来直接构造该类的对象,从而创造不同的对象,破坏单例模式
改进(以DLC懒汉模式为例)
private SingletonDemo_3 ( ) {
synchronized ( SingletonDemo_4. class ) {
if ( instance!= null) {
throw new RuntimeException ( "反射异常!!!" ) ;
}
}
}
class SingletonDemo03Test02 {
public static void main ( String[ ] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
SingletonDemo_3 instance = SingletonDemo_3. getInstance ( ) ;
Constructor< SingletonDemo_3> constructor
= SingletonDemo_3. class . getDeclaredConstructor ( null) ;
constructor. setAccessible ( true ) ;
SingletonDemo_3 instance2 = constructor. newInstance ( null) ;
System. out. println ( instance== instance2) ;
System. out. println ( instance. hashCode ( ) ) ;
System. out. println ( instance2. hashCode ( ) ) ;
}
}
结果:
Exception in thread "main" java. lang. reflect. InvocationTargetException
at java. base/ jdk. internal. reflect. NativeConstructorAccessorImpl. newInstance0 ( Native Method)
at java. base/ jdk. internal. reflect. NativeConstructorAccessorImpl. newInstance ( NativeConstructorAccessorImpl. java: 62 )
at java. base/ jdk. internal. reflect. DelegatingConstructorAccessorImpl. newInstance ( DelegatingConstructorAccessorImpl. java: 45 )
at java. base/ java. lang. reflect. Constructor. newInstance ( Constructor. java: 490 )
at com. niss. singleton. SingletonDemo03Test02. main ( SingletonDemo_3. java: 48 )
Caused by: java. lang. RuntimeException: 反射异常!!!
at com. niss. singleton. SingletonDemo_3. < init> ( SingletonDemo_3. java: 12 )
. . . 5 more
如果已经使用方法获取该单例,则再通过反射机制会抛出异常,从而保障只有一个实例 但是如果一开始就直接使用反射机制来获取实例,则同样会获取多个不同实例
class SingletonDemo03Test02 {
public static void main ( String[ ] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor< SingletonDemo_3> constructor
= SingletonDemo_3. class . getDeclaredConstructor ( null) ;
constructor. setAccessible ( true ) ;
SingletonDemo_3 instance = constructor. newInstance ( null) ;
SingletonDemo_3 instance2 = constructor. newInstance ( null) ;
System. out. println ( instance== instance2) ;
System. out. println ( instance. hashCode ( ) ) ;
System. out. println ( instance2. hashCode ( ) ) ;
}
}
结果:
false
1239731077
557041912
改进(以DLC懒汉模式为例)
private static boolean flag = false ;
private SingletonDemo_3 ( ) {
synchronized ( SingletonDemo_4. class ) {
if ( flag == false ) {
flag = true ;
} else {
throw new RuntimeException ( "反射异常!!!" ) ;
}
}
}
Exception in thread "main" java. lang. reflect. InvocationTargetException
at java. base/ jdk. internal. reflect. NativeConstructorAccessorImpl. newInstance0 ( Native Method)
at java. base/ jdk. internal. reflect. NativeConstructorAccessorImpl. newInstance ( NativeConstructorAccessorImpl. java: 62 )
at java. base/ jdk. internal. reflect. DelegatingConstructorAccessorImpl. newInstance ( DelegatingConstructorAccessorImpl. java: 45 )
at java. base/ java. lang. reflect. Constructor. newInstance ( Constructor. java: 490 )
at com. niss. singleton. SingletonDemo03Test02. main ( SingletonDemo_3. java: 55 )
Caused by: java. lang. RuntimeException: 反射异常!!!
at com. niss. singleton. SingletonDemo_3. < init> ( SingletonDemo_3. java: 16 )
. . . 5 more
通过添加一个静态标志变量来判断是否为第一次创建该实例 在获取第二个实例时,标志位为true,若通过反射直接抛出RuntimException,防止第二个实例的生成 依旧可以通过反射机制来获取私有静态flag变量,显示设为false,从而通过反射获取多个不同实例
5.通过枚举实现单例模式
package com. niss. singleton;
public enum SingletonDemo_5 {
INSTANCE;
public SingletonDemo_5 getInstance ( ) {
return INSTANCE;
}
}
class SingletonDemo_5Test {
public static void main ( String[ ] args) {
SingletonDemo_5 instance = SingletonDemo_5. INSTANCE;
SingletonDemo_5 instance2 = SingletonDemo_5. INSTANCE;
System. out. println ( instance== instance2) ;
}
}
public T newInstance ( Object . . . initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if ( ! override) {
Class< ? > caller = Reflection. getCallerClass ( ) ;
checkAccess ( caller, clazz, clazz, modifiers) ;
}
if ( ( clazz. getModifiers ( ) & Modifier. ENUM) != 0 )
throw new IllegalArgumentException ( "Cannot reflectively create enum objects" ) ;
ConstructorAccessor ca = constructorAccessor;
if ( ca == null) {
ca = acquireConstructorAccessor ( ) ;
}
@SuppressWarnings ( "unchecked" )
T inst = ( T) ca. newInstance ( initargs) ;
return inst;
}
newInstance方法代码中,可以看出如果是枚举则直接抛出异常,可以根据这点来实现可防止反射破坏的单例模式 足够简单,线程安全,调用效率高,不能延时加载