java单例模式实现
单例模式基本概念
所谓类的单例设计模式:就是采用一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个只提供一个取得其对象实例的方法。
- 整个应用中只存在一个实例。
- 该类只提供一个方法获取该实例。
java实现单例模式的7种方式
- 1、饿汉式-静态变量
- 2、饿汉式-静态代码块
- 3、懒汉式-单线程方式
- 4、懒汉式-同步方法
- 5、懒汉式-同步代码块,双重检查
- 6、懒汉式-静态内部类
- 7、懒汉式-枚举
1、 饿汉式-静态变量
- 构造器私有化,使得外部不能new,也不能继承。
- 在本类内部创建对象实例。
- 提供一个公有的静态方法,用于返回实例对象。
public class Singleton {
private Singleton(){
}
private final static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}
饿汉式-静态变量优缺点
- 写法简单,单例在类加载的时候完成实例化;利用类加载机制,避免了线程同步问题。
- 但是在类加载的时候就完成了实例化,没有达到懒加载的效果。如果从始至终都没有使用过这个单例,就会造成内存浪费。
- 这种基于classloader机制避免了线程同步问题,不过,instance在类加载时就实例化了,在单例模式中大多数都是调用了getInstance方法。但是导致类加载的原因有很多,不能确定其他方法(或静态方法)导致了类的加载,这时候初始化instance就meiyou达到懒加载的效果。
结论:
- 这种单例模式推荐使用,不过最好是能确保该单例在应用中一定能被使用到。否则会造成内存浪费。
2、饿汉式-静态代码块
public class Singleton {
private static Singleton instance;
static {
instance = new Singleton();
}
private Singleton(){ }
public static Singleton getInstance(){
return instance;
}
}
静态变量初始化 与 静态代码块 的初始化时机类似。都是利用了classloader机制来避免线程同步问题。
3、懒汉式-单线程方式
public class Singleton {
//创建静态变量
private static Singleton instance;
//私有化构造方法
private Singleton(){ }
//在获取实例的公有方法中去判断是否存在对象,如果存在就返回,不存在就创建。
public static Singleton getInstance(){
if( instance == null ){
instance == new Singleton();
}
return instance;
}
}
懒汉式-单线程方式的优缺点
- 这种方式起到了懒加载创建单实例的效果,但是只能在单线程语言下使用。比如js。
- 如果在多线程下,可能有多个线程进入到if( instance == null ){ … }的代码块内,从而会创建多个实例。所以在多线程环境下不可使用这种方式。
结论
- 对于多线程语言: 这种懒汉式不推荐使用,有创建多个实例的潜在风险。
4、懒汉式-同步方法
public class Singleton {
private static Singleton instance;
private Singleton(){}
//提供一个静态的公有方法,加入同步处理的代码synchronized,解决线程安全问题。
public static synchronized Singleton getInstance(){
if( instance == null ){
instance = new Singleton();
}
return instance;
}
}
懒汉式-同步方法 优缺点:
- 使用同步方法解决了线程不安全问题。且也是懒加载方式。
- 但是效率太低,每个线程在想获取类的实例的时候,执行getInstance()方法都要进行同步。但实际上同步只需要在第一次执行这个方法的时候就够了,以后获取该实例的时候,直接return就行。
结论:
- 实际开发过程中,不推荐使用这种方式。
5、懒汉式-同步代码块
public class Singleton {
private static volatile Singleton instance;
public Singleton(){}
public static Singleton getInstance(){
if( instance == null ){
synchronized ( Singleton.class ){
if( instance == null ){
instance = new Singleton();
}
}
}
}
return instance;
}
懒汉式-同步代码块 优缺点
- Double-check概念在多线程开发过程中常使用到。如代码中所示,我们进行两次if(instance === null)检查,这样就能保证线程安全。
- Double-check的方式,实例化代码只执行一次,后面再次访问时,判断if(instance == null){ … }直接return实例化对象,也避免了反复进行方法同步的判断。
- volatile是轻量级的synchronized,能够保证Singleton对象创建之后,立刻被其他线程知道。volatile关键字只能修饰变量。
结论
- 懒加载,线程安全,效率高。
- 在实际使用过程中,推荐使用这种方式。
6、懒汉式-静态内部类
静态内部类的特点:
- 外部类被装在时,静态内部类不会被装载。
- 只有外部类的方法调用时,方法内部如果调用静态内部类的方法或者成员变量,该静态内部类才会被装载,而且只会被装载一次。且能利用classloader机制来保证线程安全。
public class Singleton {
private Singleton(){}
private static class SingletonInstance {
//内部类能获取外部类的私有方法,并且使用。
private static final INSTANCE = new Singleton();
}
//当第一次调用这个方法时,触发了静态内部类的加载,同时在静态内部类中创建了Singleton实例。以后的调用就不会再触发类加载,而是直接返回Singleton实例。
private static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
说明
- 通过静态内部类方式创建单例,是利用了classloader机制来保证初始化实例时避免了线程同步问题。
- 静态内部类方式在Singleton类被加载时并不会立刻实例化,而是在需要实例化时,调用getInstance方法,才会进行Singleton的实例化。
- 类的静态属性只会在第一次加载类的时候进行初始化,所以在这里,jvm帮助我们保证了线程的安全,在类进行初始化时,别的线程是无法进入的。
优点
- 懒加载,线程安全,效率高。
- 推荐使用。
七、 单例-枚举形式
- 枚举的属性就是一个实例。
- 借助JDK1.5中添加的枚举来实现单例模式,不仅能避免多线程同步问题,而且还能防止反序列化重新新的对象。
public enum Singleton {
INSTANCE;
public void method(){
...
}
}
结论
- 最推荐使用的方式。
单例模式在JDK中的使用
- jdk中Runtime就是使用饿汉式创建的单例。利用classloader的机制保证线程安全。
public class Runtime {
private static Runtime currentRuntime = new Runtime();
private Runtime(){}
public static Runtime getRuntime(){
return currentRuntime;
}
}