Java设计模式--单例模式(5)

单例模式的定义:确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一的实例。

单例模式是一种对象创建型模式,主要是3个要点: (1)只能有一个实例;(2)必须是自行创建这个实例;(3)必须自行向整个系统提供这个实例。

 

单例模式代码实现:

package com.create.single;

public class Singleton {

    //静态私有成员变量
    private static Singleton singleton = null;

    //私用构造函数
    private Singleton() {

    }
    //公用静态成员方法,返回唯一实例
    public static Singleton getSingleton() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

 

测试类:

package com.create.single;

public class Client {

    public static void main(String[] args) {
        Singleton s1 = Singleton.getSingleton();
        Singleton s2 = Singleton.getSingleton();
        System.out.println(s1 == s2);
    }
}

 

运行结果:true

 

饿汉式单例和懒汉式单例

1,饿汉式单例模式是实现最简单的单例类,由于在定义静态变量的时候实例化单例类,因此在类加载的时候单例对象就已经创建了。

代码如下:

 

package com.create.single;

/**
 * 饿汉模式
 */
public class HungrySingleton {

    //定义静态变量
    private static final HungrySingleton singleton = new HungrySingleton();

    //私有化构造函数
    private HungrySingleton(){};

    //返回实例方法
    public static HungrySingleton getSingleton() {
        return singleton;
    }
}

2,懒汉式单例

与饿汉式相同的是类的构造函数都是私用的,不同的是懒汉式单例类在第一次被引用时将自己实例化,在类加载时不会被实例化,而饿汉式时是在类加载的时候就已经实例化了。

 

懒汉式单例代码:

package com.create.single;

public class LazySingleton {

    public static LazySingleton singleton = null;

    private LazySingleton(){};

    //加锁解决并发问题
    public synchronized static LazySingleton getSingleton() {
        if(singleton == null) {
            singleton = new LazySingleton();
        }
        return singleton;
    }
}

加入synchronized锁虽然解决了线程安全问题,但是每次调用getSingleton()方法每次都需要进行线程锁判断,在多线程高并发环境中将导致系统性能降低的问题。

 

下面我们来使用双重检查锁定来另外实现懒汉式单例:

package com.create.single;

public class LazySingleton {

    public volatile static LazySingleton singleton = null;

    private LazySingleton(){};

    public static LazySingleton getSingleton() {
        //第一重判断
        if(singleton == null) {
            //锁定代码块
            synchronized (LazySingleton.class) {
                //第二重判断
                if(singleton == null) {
                    singleton = new LazySingleton();
                }
            }
        }
        return singleton;
    }
}

第二种实现虽然也加了锁,但是实现效率比第一种要好。但是由于volatile关键字会屏蔽JVM所做的一些代码优化,可能会导致系统的运行效率降低。所以这也不是最完美的实现方式。

 

饿汉式单例和懒汉式单例的比较:

       饿汉式单例类在类加载的时候就将自己实例化了,优点是无需考虑多个线程并发问题,可以确保实例的唯一性。缺点是由于系统加载时需要创建饿汉式单例对象(资源利用率比较低),加载时间可能会比较长。

       懒汉式单例类是在第一次使用的时候创建,无需一直占用资源,实现了延迟加载。但是由于会有多并发问题存在,所以使用双重检查锁定等机制进行控制,这将导致性能会受到一定的影响。

 

下面我们通过静态内部类的方式实现单例模式

package com.create.single;

/**
 * 通过静态内部类的方式实现单例
 */
public class StaticSingleton {

    //私有化构造函数
    private StaticSingleton() {};

    //静态内部类
    private static class HolderClass {
        private final static StaticSingleton singleton = new StaticSingleton();
    }

    //提供对外调用接口
    private static StaticSingleton getSingleton() {
        return HolderClass.singleton;
    }

    //测试
    public static void main(String[] args) {
        StaticSingleton s1 = StaticSingleton.getSingleton();
        StaticSingleton s2 = StaticSingleton.getSingleton();
        System.out.println(s1 == s2);
    }
}

运行结果为:true

这种方式在类加载时不会被实例化,在第一次调用时会加载HolderClass静态内部类,此时会初始化这个成员变量,由JVM来保证其线程的安全,确保只会被初始化一次,由于没有加锁操作,所以该性能不会受到其它影响

 

通过这种方式既能实现延迟加载,又能保证线程安全,不影响系统性能,是一种比较完美的实现方式。

 

单例模式的优缺点

       优点: (1)提供了唯一实例的受控访问,因为单例类封装了它的唯一实例,所以它可以控制客户端怎样以及何时访问它。

                   (2)节约系统资源,对于一些频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。

       缺点: (1)单例类没有抽象层,因此扩展难度大。

                   (2)一定程度上违背了单一职责,将对象的创建和对象本身的功能耦合在一起。

                   (3)由于自动垃圾回收机制的存在,如果实例化对象长时间不被利用,系统会认为时垃圾对象,会自动销毁并回收                              资源,下次利用时又重新实例化,这会导致共享的实例化对象状态的丢失。

 

 

 

 

 

你可能感兴趣的:(Java设计模式)