设计模式之单例模式

来源:互联网 发布:网络有利还是有害 编辑:程序博客网 时间:2024/06/05 01:58

单例模式类图

一、核心作用:

保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

二、优点:

1、由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决;
2、单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。

三、应用场景:

1、Windows的Task Manager(任务管理器)就是很典型的单例模式;
2、Windows的Recycle Bin(回收站)也是典型的单例应用,在整个系统运行过程中,回收站一直维护着仅有的一个实例;
3、项目中,读取配置文件的类,一般也只有一个对象,没有必要每次使用配置文件数据,每次new一个对象去读取;
4、网站的计数器,一般也是采用单例模式实现,否则难以同步;
5、应用程序的日志应用,一般都采用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加;
6、数据连接池的设计一般也是采用单例模式,因为数据连接是一种数据库资源;
7、操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统;
8、在servlet编程中,每个Servlet也是单例;

四、常见的五种单例模式实现方式:

主要:
-饿汉式(线程安全,调用效率高。但是,不能延时加载)
-懒汉式(线程安全,调用效率不高。但是,可以延时加载)
其它:
-双重检测式(由于JVM底层内部模型原因,偶尔会出问题。不建议使用)
-静态内部类式(线程安全,调用效率高。但是,可以延时加载)
-枚举单例式(线程安全,调用效率高,不能延时加载)

五、代码(java实现):

/** * 饿汉式单例模式 *      线程安全,调用效率高。但是,不能延时加载 * @author ly1 * */public class Singleton1 {    private static Singleton1 instance  = new Singleton1();    private Singleton1(){    }    public static Singleton1 getInstance(){        return instance;    }}
/** * 懒汉式单例模式 *      线程安全,调用效率不高。但是,可以延时加载 * @author ly1 * */public class Singleton2 {    private static Singleton2 instance;    private Singleton2(){    }    public static synchronized Singleton2 getInstance(){        if(instance == null){            instance = new Singleton2();        }        return instance;    }}
/** * 双重检测单例模式 *      由于JVM底层内部模型原因,偶尔会出问题。不建议使用 * @author ly1 * */public class Singleton3 {    private static Singleton3 instance;    private Singleton3(){    }    public static Singleton3 getInstance(){        if(instance == null){            synchronized(Singleton3.class){                if(instance == null){                    instance = new Singleton3();                }            }        }        return instance;    }}
/** * 静态内部类单例模式 *      线程安全,调用效率高,可以延时加载,结合了饿汉式与懒汉式的优点 * @author ly1 * */public class Singleton4 {    private static class Holder{        private static final Singleton4 instance = new Singleton4();    }    public Singleton4 getInstance(){        return Holder.instance;    }    private Singleton4(){    }}
/** * 枚举实现单例模式 *      线程安全,调用效率高,不能延时加载。JVM底层保证单例。 * @author ly1 * */public enum Singleton5 {    INSTANCE;    //其它操作    private void operation(){    }}
/** * 单例模式的测试,这里只测试第一个饿汉式单例模式 * @author ly1 * */public class Client {    public static void main(String[] args) {        Singleton1 s1 = Singleton1.getInstance();        Singleton1 s2 = Singleton1.getInstance();        System.out.println(s1 == s2);    }}结果:true

六、五种单例模式调用效率的测试:

/** * 测试五种单例模式的调用效率,这里测的是第一种,其它的类似 * @author ly1 * */public class PerformanceTest {    public static void main(String[] args) throws Exception {        int threadNum = 10;        //记录开始时间        long start = System.currentTimeMillis();        //管理线程        final CountDownLatch count = new CountDownLatch(threadNum);        //开启十个线程,每个线程调用1亿次        for (int i = 0; i < threadNum; i++) {            new Thread(new Runnable(){                @Override                public void run() {                    for (int j = 0; j < 100000000L; j++) {                        Object obj = Singleton5.INSTANCE;                    }                    count.countDown();                }            }).start();        }        //保证main线程在这十个线程执行完后再执行        count.await();        //记录结束时间        long end = System.currentTimeMillis();        System.out.println("总耗时:" + (end - start) + "ms");    }}

测试结果:

方式 效率 饿汉式 100ms 懒汉式 37183ms 双重检测式 104ms 静态内部类式 99ms 枚举式 99ms

分析:

1、可以看出懒汉式由于在方法上加了同步锁,导致每次调用都要同步,性能大大降低。
2、如果创建对象资源耗时,要求延时加载,建议选择静态内部类式;
3、如果不要求延时加载,建议选择枚举式。

七、其他相关问题

以上五种方式,除了枚举式,其他的都可以用反射和反序列化破解,造成不是单例。
-反射,虽然构造器私有化,依然还是可以调用。(参考)
-反序列化,先将对象通过对象流写入到文件中,再用对象流读取,会创建新的对象。(关于对象流)
当然,写普通的项目,不用考虑这些,这里只是提一下。

0 0
原创粉丝点击