单例设计模式

不想你离开。 提交于 2020-01-15 05:19:15

什么是单例?
一个类仅有一个实例 解决了全局使用的类频繁的创建与销毁

饿汉式

public class SingletonOne {
    // 私有化构造方法
    private SingletonOne(){};
    
    // 性能不高 在程序启动的时候 就创建好了对象 对象一直在内存中
    // 线程是安全的
    public static final SingletonOne hungry = new SingletonOne();
    public static SingletonOne getHungry(){
        return hungry;
    }
}

测试

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
    // 200 个线程并发访问
     int count = 200;
        CountDownLatch latch = new CountDownLatch(count);
        for (int i = 0; i < count; i++){
            new Thread(){
                @Override
                public void run() {
                    Object register = SingletonOne.getHungry();
                    System.out.println(System.currentTimeMillis() + ":" + register);
                    try {
                        latch.await();  // 所有线程堵塞在这里  等到停止循环的时候 全部放行 
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }.start();
            latch.countDown();
        }
}

可以看出饿汉式都是同一个对象 线程是安全的
在这里插入图片描述

懒汉式

// 性能比饿汉式高 需要的时候才创建  延时加载
// 多个线程并发访问 线程不安全 解决方法就是加锁  加锁后性能低
public class SingletionTwo {
    private SingletionTwo(){
        
    }
    public static SingletionTwo lazyLoad = null;
    public static  SingletionTwo getLazyLoad(){
        if(lazyLoad == null){
            lazyLoad = new SingletionTwo();
        }
        return lazyLoad;
    }
}

还是用上面的测试方法

在这里插入图片描述
可以看出懒汉式并不能保证每次创建同一个实例 线程并发访问的时候 会创建新的对象 解决方法只能加锁

// 性能比饿汉式高 需要的时候才创建  延时加载
// 多个线程并发访问 线程不安全 解决方法就是加锁  加锁后性能低
public class SingletionTwo {
    private SingletionTwo(){
        
    }
    public static SingletionTwo lazyLoad = null;
    //  这里加了锁
    public static synchronized SingletionTwo getLazyLoad(){
        if(lazyLoad == null){
            lazyLoad = new SingletionTwo();
        }
        return lazyLoad;
    }
}

注册登记式

// 注册登记式 
// 每使用一次 都往Map中添加 下次取对象的时候 先从Map中取 以保证获取的都是同一个对象
// 注册登记式  每使用一次 都往Map中添加 下次取对象的时候 先从Map中取 以保证获取的都是同一个对象
public class Register {
    private static Map<String,Object> map = new ConcurrentHashMap<>();
    private Register(){};
    public static Object getRegister(String name){
        if(map.containsKey(name)){
            return map.get(name);
        }else{
            Object o = null;
          try {
               o = Class.forName(name).newInstance();
              map.put(name,o);
          }catch (Exception e){
              e.printStackTrace();
          }
          return o;
        }
    }
}

先随便创建一个对象

public class Tgg {
}

测试代码

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
     int count = 200;
        CountDownLatch latch = new CountDownLatch(count);
        for (int i = 0; i < count; i++){
            new Thread(){
                @Override
                public void run() {
                    Object register = Register.getRegister("Singe.Tgg"); // 刚刚创建类的路径
                    System.out.println(System.currentTimeMillis() + ":" + register);
                    try {
                        latch.await();  // 所有线程堵塞在这里  等到停止循环的时候 全部放行
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }.start();
            latch.countDown();
        }

可以看出 这个还离谱一些 有得甚至没拿到值 解决这个问题只能加锁
在这里插入图片描述

// 注册登记式  每使用一次 都往Map中添加 下次取对象的时候 先从Map中取 以保证获取的都是同一个对象
public class Register {
    private static Map<String,Object> map = new ConcurrentHashMap<>();
    private Register(){};
    // 这里加了锁
    public static synchronized Object getRegister(String name){
        if(map.containsKey(name)){
            return map.get(name);
        }else{
            Object o = null;
          try {
               o = Class.forName(name).newInstance();
              map.put(name,o);
          }catch (Exception e){
              e.printStackTrace();
          }
          return o;
        }
    }
}

执行完测试类后 可以看到创建的都是同一个对象
在这里插入图片描述

内部类单例

// 在外部类调用时候 内部类才会被加载  在方法之前就初始化
public class SingletionThree {
    private static boolean init = false;
    private SingletionThree(){
        synchronized (SingletionThree.class){
            if(init == false){
                init = !init;
            }else{
                throw new RuntimeException("单例被侵犯");
            }
        }
    }
    
    public static final SingletionThree getInstance(){return LayHodler.LAZY;}
    private static class LayHodler{
        private static final SingletionThree LAZY = new SingletionThree();
    }
}

内部类单例是线程安全的 因为只有在外部类被调用的时候内部类才会被加载 不过通过反射可以修改构造方法 创建新的实例

序列化与反序列化保证单例

// 序列化与反序列化保证单例 重写readResolve 方法
public class Seriable implements Serializable {
    public final static Seriable INSTANCE = new Seriable();
    private Seriable(){};
    public static Seriable getInstance(){
        return INSTANCE;
    }
    
    private Object readResolve(){
        return INSTANCE;
    }
}

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
     Seriable seriable = null;
      Seriable seriable1 = Seriable.getInstance();
        FileOutputStream outputStream = new FileOutputStream("F://Seriable.obj");
        ObjectOutput objectOutput = new ObjectOutputStream(outputStream);
        objectOutput.writeObject(seriable1);
        objectOutput.flush();
        objectOutput.close();
        
       FileInputStream fileInputStream = new FileInputStream("F://Seriable.obj");
       ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        Seriable seriable2 = (Seriable) objectInputStream.readObject();
        objectInputStream.close();
        System.out.println(seriable1);
        System.out.println(seriable2);
        System.out.println(seriable2 == seriable1);*/
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!