单例设计模式

介绍

  • 作用:保证类只有一个对象,并提供一个访问该实例的方法
  • 应用场景:任务管理器,读取配置文件的类,日志管理,数据库连接池等
  • 优点:避免全局使用的类频繁创建与销毁,节省系统资源与内存开销
  • 缺点:构造器私有后不能继承

实现方式(线程安全)

  • 懒汉式:延时加载,调用效率太低
class SingleInstance{
    private static SingleInstance instance=null;
    private SingleInstance(){};
    public  synchronized static SingleInstance getInstance(){ //synchronized效率低
        if(instance==null){
            instance=new SingleInstance();//调getInstance()才实例化
        }
        return instance;
    }
}
  • 饿汉式:调用效率高,不能延时加载
class SingleInstance{
    private static SingleInstance instance=new SingleInstance();//类装载时就实例化;classloder机制保障线程安全
    private SingleInstance(){};
    public  static SingleInstance getInstance(){ //不需要synchronized就可以保证线程安全,调用效率高
        return instance;
    }
}
  • 双检锁:调用效率高,可以延时加载
class SingleInstance{
    private static SingleInstance instance=null;
    private SingleInstance(){};
    public   static SingleInstance getInstance(){ 
        //a,b,c,d,e
        if(instance==null){
            //a,b
            synchronized(SingleInstance.class){
                //a
                if(instance==null){
                    instance=new SingleInstance();
                }
                //b
                return instance;
            }
        }
        //c,d,e
        return instance;

    }
}
  • 静态内部类:调用效率高,可以延时加载
class SingleInstance{
    private SingleInstance(){}
    private static class Instance{//内部类在使用时才会被加载
        private static final SingleInstance instance=new SingleInstance();//classloader保障线程安全
    }
    public static SingleInstance getInstance(){
        return Instance.instance; //调用时才实例化
    }
}
  • 枚举:调用效率高,不能延时加载,但是可以防止反射,反序列化
pubic enum SingleInstance{
    INSTANCE;//通过SingleInstance.INSTANCE获取
    public void operation(){}
}

防反射

  • 问题:
public class MyLearn {
    public static void main(String[] args) throws Exception {
        SingleInstance instance1 = SingleInstance.getInstance();
        SingleInstance instance2 = SingleInstance.getInstance();
        System.out.println(instance1);
        System.out.println(instance1);
        Class<SingleInstance> clazz = (Class<SingleInstance>) Class.forName("learn.SingleInstance");        
        Constructor<SingleInstance> constructor = clazz.getDeclaredConstructor(null);
        constructor.setAccessible(true);//跳过私有权限检查
        SingleInstance instance3 = constructor.newInstance();
        SingleInstance instance4 = constructor.newInstance();
        System.out.println(instance3); //反射调用生成新对象实例
        System.out.println(instance4);
    }

}
  • 解决:
class SingleInstance{
    private static SingleInstance instance=new SingleInstance();
    private SingleInstance(){
        if(instance!=null){
            throw new RuntimeException();//抛异常
        }
    };
    public  static SingleInstance getInstance(){ 
        return instance;
    }
}

防反序列化

  • 问题:
public class MyLearn {
    public static void main(String[] args) throws Exception {
        SingleInstance instance1 = SingleInstance.getInstance();
        FileOutputStream fos = new FileOutputStream("f:/instace.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(instance1);
        oos.close();
        fos.close();
        FileInputStream fis = new FileInputStream("f:/instace.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        SingleInstance instance2 = (SingleInstance) ois.readObject();
        fis.close();
        ois.close();
        System.out.println(instance1);
        System.out.println(instance2);
    }

}
  • 解决:
class SingleInstance implements Serializable {
    private static SingleInstance instance = new SingleInstance();

    private SingleInstance() {
        if (instance != null) {
            throw new RuntimeException();
        }
    };

    public static SingleInstance getInstance() {
        return instance;
    }
    private Object readResolve() throws ObjectStreamException{//反序列化时自动执行
        return instance;
    }
}

多线程环境测试

  • 多线程测试辅助类:CountDownLatch
public class MyLearn {
    public static void main(String[] args) throws Exception {
        CountDownLatch count=new CountDownLatch(100); 
        long start = System.currentTimeMillis();
        for(int i=0;i<100;i++){
            new Thread(new Runnable() {

                @Override
                public void run() {
                    for(int j=0;j<100000;j++){                      
                        SingleInstance instance=SingleInstance.getInstance();
                    }
                    count.countDown();//计数器减一;
                }
            }).start();
        }
        count.await();//主线程阻塞,直到countDown减为零,即开启的线程全部执行完
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }

}
  • 效率比较:单例对象占用资源小,不需要延时加载,使用枚举方式好于饿汉式;单例对象占用资源大,需要延时加载,使用静态内部类方式好于懒汉式。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值