单例设计模式

158 阅读2分钟

单例设计模式是最简单的设计模式之一,这篇文章简单介绍单例设计模式作用,如何使用单例设计模式,以及一些简单的例子。

单例设计模式的作用

很多对象不需要重复创建回收,自始至终我们可以使用一个对象复用。单例设计模式就是为了防止对象的重复创建。

单例设计模式实现

饿汉式单例模式

为了使程序获取的都是同一个对象,那么可以使用静态对象的方式保证对象唯一,这就是饿汉式单例模式。
饿汉式单例模式足够简单,且线程安全,但是缺点也很明显,就是类加载的时候就会被初始化。

class Client{
    private static Client c = new Client();
    
    private Client(){}
    
    public static Client getClient(){
        return c;
    }
}

懒汉式单例模式

由于饿汉式的缺点,懒汉式单例模式应运而生,让对象在使用的时候创建。

class Client{
    private static Client c = null;
    
    private Client(){}
    
    public static Client getClient(){
        return c;
    }
}

很显然这种方式存在线程安全问题,无法完全保证对象唯一。

  1. 使用synchronized方法保证线程安全:

//懒汉式
class Client{
    private static Client c = null;
    
    private Client(){}
    
    public static synchronized Client getClient(){
        if (c = null){
            c = new Client();
        }
        return c;
    }
}
  1. 由于我们只需要在new的时候保证线程安全,所以可以使用二次判断做以下改进这样在getClient的大部分时候不需要进去synchronize中去,大大提高效率:

//懒汉式
class Client{
    private volatile static Client c = null;
    
    private Client(){}
    
    public static Client getClient(){
        if (c == null){
            synchronized (Client.class){
                if (c == null){
                    c = new Client();
                }
            }
        }
        return c;
    }
}

IoDH(Initialization Demand Holder)单例模式

IoDH单例模式整合了懒汉式和饿汉式单例模式,是最完美的单例模式实现。
饿汉式单例模式是在静态变量初始化对象,那么在JVM加载这个类的时候就会初始化静态变量,所以饿汉式会在JVM加载时创建对象。而JVM加载对象是不会初始化静态内部类,利用这个特点就可以保留饿汉式的有点,又能够lazy加载。

class Client{
    private Client(){}
    
    public static Client getClient(){
        return ClientHolder.c;
    }
    
    private static class ClientHolder{
        private final static Client c = new Client();
    }
}

枚举实现单例模式

在Effective Java中,作者极力推荐使用枚举实现单例模式。因为枚举拥有线程安全,写法简单等特点,并且java其他单例方式如果实现了序列化的接口就不能保证对象唯一。因为java序列化反序列化是通过反射实现的,反射是可以破坏私有构造函数的。而枚举不是通过反射进行序列化和反序列化的。

public class Client {
    private Client(){}

    private enum EnumHolder{
        INSTANCE;
        private final Client c;
        EnumHolder(){
            c = new Client();
        }
        private Client getClient(){
            return c;
        }
    }

    public static Client getClient(){
        return EnumHolder.INSTANCE.getClient();
    }
}