设计模式之单例模式

单例模式类图

一、核心作用:

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

二、优点:

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值