天天看点

设计模式--单例模式单例模式

单例模式

单例模式就是采取一定的方式保证在整个的软件系统中,对某个类只能存在一个实例对象,并且该类只提供一个取得其对象实例的方法。

  • 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
  • 当想实例化一个单例对象的时候,必须记住使用相应的获取对象的方式,而不是用new。
  • 单例模式使用场景:需要频繁的进行创建和销毁的对象,创建对象时耗时过多或耗费资源过多(即重量级对象),但又经常哟用到的对象、工具类对象、频繁访问数据库和文件的对象(比如数据源,session工厂等)。

1)饿汉式(静态常量)

代码:
package singleton.type01;

/**
 * @Author: [email protected]
 * 单例模式-- 饿汉式(静态常量)
 * 优点:
 * 写法简单,就是在类装载的时候就完成实例化,避免了线程同步问题
 * 缺点:
 * 在类装载的时候完成实例化,没有达到 lazy loading的效果。如果从开始
 * 至终从未使用过这个实例,则会造成内存的浪费
 * 这种方式基于classloder 机制,避免了多线程同步问题,不过 instance 在类装载
 * 时就实例化,在单例模式中大多数都是调用 getInstance() 方法,但是导致类装载
 * 的原因有很多,因此不能确定有其他的方式(或者有其他的静态方法)导致类装载,这
 * 时候初始化instance 就没有达到lazy loading的效果
 * <p>
 * 结论:
 * 这种单例可以使用,可能造成内存浪费
 */
public class SingletonTest01 {
    public static void main(String[] args) {
        // 测试
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2); // true
    }
}

// 饿汉式(静态变量)
class Singleton {
    // 1. 构造器私有化,外部不能new
    private Singleton() {

    }

    // 2. 在本类内部创建对象实例
    private final static Singleton instance = new Singleton();

    // 3.对外提供一个共有的静态方法,返回实例对象
    public static Singleton getInstance() {
        return instance;
    }
}
           

2)饿汉式(静态代码块)

代码:
package singleton.type02;

/**
 * @Author: [email protected]
 * 单例模式-- 饿汉式(静态代码块)
 * 这种方式和上面的方式类似,只不过将类实例化的过程放到静态代码块中,也是在类装载的
 * 时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面的一样
 * 结论:
 * 这种单例可以使用,但是可能造成内存浪费
 */
public class SingletonTest02 {
    public static void main(String[] args) {
        // 测试
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2); // true
    }
}

// 饿汉式(静态代码块)
class Singleton {
    // 1. 构造器私有化,外部不能new
    private Singleton() {

    }

    // 2. 在本类内部创建对象实例
    private static Singleton instance;

    static { // 在静态代码块中,创建单例对象
        instance = new Singleton();
    }

    // 3.对外提供一个共有的静态方法,返回实例对象
    public static Singleton getInstance() {
        return instance;
    }
}
           

3)懒汉式(线程不安全)

代码:
package singleton.type03;

/**
 * @Author: [email protected]
 * 懒汉式(线程不安全)
 * 起到了lazy loading的效果,但是只能在单线程下使用。
 * 如果在多线程下,一个线程进入了if(singleton == null) 判断语句块,还未来得
 * 及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多
 * 线程环境下不可使用这种方式
 * 结论:
 * 在实际开发中,不要使用这种方式
 */
public class SingletonTest03 {
    public static void main(String[] args) {
        // 测试
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2); // true
    }
}

// 懒汉式(线程不安全)
class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

    // 提供一个静态的公有方法,当使用到该方法时,才去创建instance
    // 即懒汉式
    public static Singleton getInstance() {
        if (instance == null) { // 判断该对象没有被创建则创建
            instance = new Singleton();
        }
        return instance;
    }
}
           

4)懒汉式(线程安全,同步方法)

代码:
package singleton.typte04;

/**
 * @Author: [email protected]
 * 懒汉式(线程安全,同步方法)
 * 解决了线程不安全
 * 效率太低,每个线程在想获得类的实例时候,执行getInstance() 方法都要执行同步。而
 * 其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return即可。同
 * 步效率太低
 * 结论:
 * 在实际开发中,不推荐使用这种方法
 */
public class SingletonTest04 {
    public static void main(String[] args) {
        // 测试
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2); // true
    }
}

// 懒汉式(线程安全,同步方法)
class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

    // 提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
    // 即懒汉式
    public static synchronized Singleton getInstance() {
        if (instance == null) { // 判断该对象没有被创建则创建
            instance = new Singleton();
        }
        return instance;
    }
}
           

5)懒汉式(线程安全,同步代码块)

代码:
package singleton.type05;

/**
 * @Author: [email protected]
 * 懒汉式(线程安全,同步代码块)
 * 这种方式本意是想对第四种实现方式的改进,因为前者同步方法效率低,改为同步产生实例
 * 的代码块。
 * 但是这种同步并不能起到线程同步的作用。跟第三种实现方式遇到的情况一致,假如一个
 * 线程进入了if(singleton == null) 判断语句块,还未来得及往下执行,另一个线程
 * 也通过了这个判断语句,这时便会产生多个实例。
 * 结论:
 * 在实际的开发中,不能使用该方式
 */
public class SingletonTest05 {
    public static void main(String[] args) {
        // 测试
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2); // true
    }
}

// 懒汉式(线程安全,同步方法)
class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

    // 提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
    // 即懒汉式
    public static  Singleton getInstance() {
        if (instance == null) { // 判断该对象没有被创建则创建
            synchronized (Singleton.class){ // 同步代码块
                instance = new Singleton();
            }
        }
        return instance;
    }
}
           

6)懒汉式(双重检查)

代码:
package singleton.type06;

/**
 * @Author: [email protected]
 * 懒汉式(双重检查)
 * double-check 概念是多线程开发中经常使用到的,进行了俩次if(singleton==null)检查
 * 可以保证线程安全
 * 这样,实例化代码只执行一次,后面再次访问时,判断if(singleton==null);直接return
 * 实例化对象,也避免的反复进行方法同步。
 * 线程安全,延迟加载,效率较高
 * 结论:
 * 在实际开发中,推荐使用这种单例模式
 */
public class SingletonTest06 {
    public static void main(String[] args) {
        // 测试
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2); // true
    }
}

// 懒汉式(线程安全,同步方法)
class Singleton {
    private static volatile Singleton instance;

    private Singleton() {

    }

    // 提供一个静态的公有方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题
    // 同时保证效率,推荐使用
    public static Singleton getInstance() {
        if (instance == null) { // 判断该对象没有被创建则创建
            synchronized (Singleton.class) { // 同步代码块
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
           

7)懒汉式(静态内部类)

代码:
package singleton.type07;

/**
 * @Author: [email protected]
 * 懒汉式(静态内部类)
 * 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
 * 静态内部类方式在Singleton类被加载的时并不会立即被实例化,而是在需要实例化时,
 * 调用getInstance方法,才会装载 SingletonInstance 类,从而完成Singleton实例化
 * 类的静态属性只会在第一次加载类的时候初始化,所以这里,JVM帮助我们保证了线
 * 程的安全性,在类进行初始化时,别的线程无法进入的。
 * 优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
 * 结论:推荐使用
 */
public class SingletonTest07 {
    public static void main(String[] args) {
        // 测试
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2); // true
    }
}

// 懒汉式(静态内部类完成) 推荐使用
class Singleton {
    private static volatile Singleton instance;

    // 构造器私有化
    private Singleton() {

    }

    // 写一个静态内部类,该类中有一个静态属性 Singleton
    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    // 提供一个静态的公有方法,直接返回
    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}
           

懒汉式(枚举)

代码:
package singleton.type08;

/**
 * @Author: [email protected]
 * 懒汉式(枚举)
 * 借助JDK1.5中添加的枚举来实现单例模式,不仅能避免多线程同步问题,
 * 而且还能防止反序列化重新创建新的对象
 * 这种方式是Effective Java 作者Josh Bloch 提倡的方法
 * 结论:推荐使用
 */
public class SingletonTest08 {
    public static void main(String[] args) {
        // 测试
        Singleton instance1 = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance1 == instance2); // true
        instance1.sayOK();
    }
}

// 枚举
enum Singleton {
    INSTANCE;

    public void sayOK() {
        System.out.println("ok");
    }
}
           

继续阅读