Java中如何实现单例模式

简介:

Java中,单例模式通常有2种分类饿汉模式和懒汉模式。

饿汉模式指的是单例实例在类装载时就被创建了。

懒汉方式值的是单例实例在首次使用时才被创建。

无论是饿汉模式还是懒汉模式,都是用了一个静态成员变量来存放真正的实例。并且私有化构造函数,防止被外部实例化。

单例(饿汉模式)代码:

1
2
3
4
5
6
7
8
9
10
11
public  class  Singleton {
     private  final  static  Singleton INSTANCE =  new  Singleton();
 
     //私有化构造方法,防止被实例化   
     private  Singleton() {
     }
 
     public  static  Singleton getInstance() {
         return  INSTANCE;
     }
}


单例懒汉模式代码,注意静态字段声明的时候,有一个volatile关键字,并且代码中两次判断是否是null,这种双重检测的机制为了应对多线程环境。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public  class  Singleton {
     private  static  volatile  Singleton INSTANCE =  null ;
 
     //私有化构造方法,防止被实例化
     private  Singleton() {
     }
 
     //双重检测
     public  static  Singleton getInstance() {
         if  (INSTANCE ==  null ) {  //①
 
             synchronized  (Singleton. class ) {  //②
 
                 if  (INSTANCE ==  null ) {  //③
                     INSTANCE =  new  Singleton();  //④
                 }
             }
         }
 
         return  INSTANCE;
     }
}


需要注意的是,即使这种Doublecheck在C++中有效,但对JAVA5之前的代码还是有一点问题的。

原因在于,编译器会优化代码,可能导致赋值语句乱序执行,上述代码中,如果有2个线程A,B。A线程已经进入4位置,当4位置的代码执行时,需要注意 INSTANCE = new Singleton()这个行代码不是一个原子操作。

JVM可能在完成Singleton类的构造方法之前,会先把一块还未初始化完成的内存地址先分配给INSTANCE,而此时如果线程B进入1位置,会认为INSTANCE已经存在,从而返回了一个未初始化完成的内存块,这可能导致程序崩溃。正是由于是先给INSTANCE赋值在初始化内存块,还是先初始化内存块再复制给INSTANCE,这个顺序无法保证,所以这种机制会出现问。所以在JAVA5之后,扩充了 volatile关键字,确保一个变量写入和读取操作的顺其不会被编译器优化成乱序,volatile变量也不会被缓存到cpu寄存器中,保证了其读取的一致性。

这和C#中的volatile的关键字是一样的作用。

除了上面2中常见的方法之外,还有其他方法。比如下面一种比较经典的实现方法,使用一个内部类,JVM自身保证了自身安全,这个模式也是在《Effective Java》这本书中推荐的,这种方式不依赖于java版本。

1
2
3
4
5
6
7
8
9
10
11
12
public  class  Singleton {
     private  Singleton() {
     }
 
     public  static  final  Singleton getInstance() {
         return  InnerClass.INSTANCE;
     }
 
     private  static  class  InnerClass {
         private  static  Singleton INSTANCE =  new  Singleton();
     }
}


但在JAVA5之后,最简单的单例实现方法是使用enum类型。

enum关键字是JAVA5中新增的,它和class,interface一样,也是一种数据类型。可以把它看成是一种特殊的类。可以在enum内部实现构造方法,字段,方法等,还可以实现接口。不过也有一些限定,枚举类中的构造器,默认为private修饰,且只能使用private。枚举类的所有实例必须在类中的第一行显式列出,否则这个枚举类不可能产生实例。JVM保证了这个每个枚举值只被初始化一次。正是由于这样一个特点,我们可以用如下代码可以实现单例。

1
2
3
4
5
public  enum  Singleton {INSTANCE;
     public  void  dosth(String arg) {
         // 逻辑代码
     }
}


上述单例中,Singleton.INSTANCE就表示了一个单例。非常简单高效。

注意Java中enum关键字和C#中的enum,差别很大,C#中不能使用这种方式。

















本文转自cnn23711151CTO博客,原文链接: http://blog.51cto.com/cnn237111/1641192,如需转载请自行联系原作者


相关文章
|
4月前
|
Java 编译器
单例模式---JAVA
“饿汉”模式 “懒汉”模式
101 1
|
14天前
|
设计模式 安全 Java
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
|
1月前
|
SQL 设计模式 安全
Java单例模式几种写法以及代码案例拿来直接使用
Java单例模式几种写法以及代码案例拿来直接使用
34 0
|
1天前
|
设计模式 消息中间件 安全
【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列
【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列
8 0
|
13天前
|
设计模式 存储 安全
Java 设计模式:深入单例模式的理解与应用
【4月更文挑战第27天】单例模式是一种常用的设计模式,在 Java 开发中扮演着重要角色。此模式的主要目的是保证一个类只有一个实例,并提供一个全局访问点。
19 0
|
16天前
|
设计模式 安全 Java
[设计模式Java实现附plantuml源码~创建型] 确保对象的唯一性~单例模式
[设计模式Java实现附plantuml源码~创建型] 确保对象的唯一性~单例模式
|
26天前
|
设计模式 存储 Java
Java设计模式:解释一下单例模式(Singleton Pattern)。
`Singleton Pattern`是Java中的创建型设计模式,确保类只有一个实例并提供全局访问点。它通过私有化构造函数,用静态方法返回唯一的实例。类内静态变量存储此实例,对外仅通过静态方法访问。
16 1
|
5月前
|
设计模式 Java
Java设计模式【一】:单例模式
Java设计模式【一】:单例模式
29 0
|
1月前
|
设计模式 缓存 安全
23种设计模式,单例模式的概念优缺点以及JAVA代码举例
【4月更文挑战第9天】在软件工程中,设计模式是为常见问题提出的典型解决方案。总共有23种设计模式,这些模式被分为三大类:创建型、结构型和行为型。单例模式是其中的一种创建型模式,它的主要目的是确保一个类只有一个实例,并提供一个全局访问点来获取这个实例
18 7
|
1月前
|
安全 Java
Java四种单例模式优劣势以及线程安全
Java四种单例模式优劣势以及线程安全
17 1