设计模式-单例

105 阅读1分钟

根据单例对象的初始化时间分为两类

1、饿汉

饿汉单例模式在类加载时就初始化创建单例对象

实现方式:在声明变量时就初始化

public class Hungry {
    private static Hungry INSTANCE=new Hungry();
    //饿汉:在加载类时就初始化单例对象
    private Hungry(){}
​
    public static Hungry getINSTANCE(){
        return  INSTANCE;
    }
​
    public void d(){
        System.out.println("do");
    }
​
    public static void main(String[] args) {
        Hungry h1= Hungry.getINSTANCE();
        Hungry h2=Hungry.getINSTANCE();
        h1.d();
        System.out.println(h1==h2);
    }
}

缺点:加载就初始化对象,可能浪费内存

2、懒汉

类加载时不进行初始化,在使用单例对象时才初始化创建对象

实现方式:使用时判断

public class Lazy {
    private static Lazy INSTANCE;
    private Lazy(){};
    public static Lazy getInstance(){
        if(INSTANCE==null) //加载时才初始化对象,线程安全问题,因为if判断和new不是原子的,可能有多i个线程判断完去new线程
            INSTANCE = new Lazy();
        return INSTANCE;
    }
}

缺点:由于访问时才创建对象,不同线程在访问时由于同步问题可能

解决方式:

  • 加线程锁判断
public class Syn {
    private static Syn INSTANCE;
    private Syn(){};
    public static  Syn getINSTANCE() {
        if (INSTANCE == null)
            synchronized (Syn.class) {
                if (INSTANCE == null)
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                INSTANCE = new Syn();
            }
        return INSTANCE;
    }
}
  • 内部类
public class Inner {
    private Inner(){}
​
    private class In{
        private  final static Inner INSTANCE = new Inner();
        //在内部类中加载外部的实例,外部线程在加载时不加载内部类,在调用getINSTANCE时才加载内部类,既做到了懒汉加载,又保证了线程安全
    }
    public static  Inner getInstance(){
        return In.INSTANCE;
    }
}
  • 为枚举类定义方法,枚举成员作为对象使用(枚举类的加载方式???)
public enum Enum {      //给枚举类定义方法
    INSTANCE;
    public void m(){}
}