【JAVA】 什么是Singleton?饿汉式与懒汉式详解

【JAVA】 什么是Singleton?饿汉式与懒汉式详解


饿汉式:
在类初始化的时候直接创建实例对象,不管你是否需要这个对象都会创建
不会有线程安全的问题
(1)构造器私有化
(2)自行创建,并且用静态变量保存
(3)向外提供这个实例
(4)强调这是一个单例,我们可以用final修饰

public class Singleton1 {

    /**
     * public 向外提供这个实例,final强调这是一个单例
     */
    public static final Singleton1 INSTANCE = new Singleton1();

    /**
     * 私有化构造器
     */
    private Singleton1(){

    }
}

饿汉式:
枚举类型:表示该类型的对象是有限的几个
我们可以限定为一个,就成了单例
最简洁的写法

public enum  Singleton2 {
    INSTANCE;
}

饿汉式:
静态代码块类型的单例
常用于需要加载有配置文件的场景下

public class Singleton3 {

    public static final Singleton3 INSTANCE ;

    /**
     *  定义要加载的配置文件的变量
     */
    private String info;

    static {
        try {
            //创建 Properties 配置文件对象
            Properties pro = new Properties();

            //对象调用 getClassLoader()方法加载   getResourceAsStream获取此配置文件
            pro.load(Singleton3.class.getClassLoader().getResourceAsStream("single.properties"));

            //将此配置文件复制给此实例
            INSTANCE = new Singleton3(pro.getProperty("info"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 创建有参构造进行赋值
     * @param info
     */
    Singleton3(String info){
        this.info = info;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "Singleton3{" +
                "info='" + info + '\'' +
                '}';
    }
}
  • 配置文件:
    【JAVA】 什么是Singleton?饿汉式与懒汉式详解_第1张图片

单例的模式的测试

public class SingleTest {
    public static void main(String[] args) {

        Singleton1 instance1 = Singleton1.INSTANCE;
        System.out.println(instance1);

        Singleton2 instance2 = Singleton2.INSTANCE;
        System.out.println(instance2);

        Singleton3 instance3 = Singleton3.INSTANCE;
        System.out.println(instance3);
    }
}

懒汉式:
延迟创建这个实例对象
(1)构造器私有化
(2)用一个静态变量保存这个唯一的实例
(3)提供一个静态方法,获取这个实例对象
使用于单线程

public class Singleton4 {

    private static Singleton4 instance;

    private Singleton4(){

    }

    /**
     * 需要此对象时才创建对象,但是有 出现线程不安全的风险
     * @return
     */
    public static Singleton4 getInstance(){
        if(instance == null){
            instance = new Singleton4();
        }
        return instance;
    }
}

使用synchronized 关键字解决线程安全问题

public class Singleton5 {

    private static Singleton5 instance;

    private Singleton5(){

    }

    /**
     * 需要此对象时才创建对象,但是有 出现线程不安全的风险
     * @return
     */
    public static Singleton5 getInstance(){
        /**
         * 使用 synchronized 关键字解决线程安全问题
         */
        synchronized (Singleton5.class){
            if(instance == null){
                instance = new Singleton5();
            }
        }
        return instance;
    }
}

懒汉式最简洁的写法: 在内部类被加载和初始化时,才创建INSTANCE实例对象 静态内部类不会自动随着外部类的加载和初始化,它是要单独去加载和初始化的 因为是在内部类加载和初始化时,创建的,因此线程是安全的

public class Singleton6 {

    private Singleton6(){

    }

    private static class Inner{
        private static final Singleton6 INSTANCE = new Singleton6();
    }

    public static Singleton6 getInstance(){
        return Inner.INSTANCE;
    }
}

懒汉式的测试 1:

public class LazySingleTest {
    public static void main(String[] args) throws Exception {
        /*Singleton4 s1 = Singleton4.getInstance();
        Singleton4 s2 = Singleton4.getInstance();
        System.out.println(s1 == s2);
        System.out.println(s1);
        System.out.println(s2);*/
        /**
         * 进行多线程测试,出现线程不安全
         */
        Callable<Singleton4> c = new Callable<Singleton4>() {
            @Override
            public Singleton4 call() throws Exception {
                return Singleton4.getInstance();
            }
        };

        /**
         * 创建线程池
         */
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<Singleton4> f1 = es.submit(c);
        Future<Singleton4> f2 = es.submit(c);
        Singleton4 s1 = f1.get();
        Singleton4 s2 = f2.get();
        System.out.println(s1 == s2);
        System.out.println(s1);
        System.out.println(s2);

        //关闭线程池
        es.shutdown();

    }
}

懒汉式的测试 2,保证线程安全:

public class LazySingle5Test {
    /**
     * 调用线程安全
     * @param args
     * @throws Exception
     */
    public static void main(String[] args)throws Exception {
        Callable<Singleton5> c = new Callable<Singleton5>() {
            @Override
            public Singleton5 call() throws Exception {
                return Singleton5.getInstance();
            }
        };

        /**
         * 创建线程池
         */
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<Singleton5> f1 = es.submit(c);
        Future<Singleton5> f2 = es.submit(c);
        Singleton5 s1 = f1.get();
        Singleton5 s2 = f2.get();
        System.out.println(s1 == s2);
        System.out.println(s1);
        System.out.println(s2);

        //关闭线程池
        es.shutdown();
    }
}

懒汉式测试 3:使用静态内部类创建线程安全单例

public class LazySingle6Test {
    public static void main(String[] args) {
        Singleton6 s1 = Singleton6.getInstance();
        Singleton6 s2 = Singleton6.getInstance();
        System.out.println(s1 == s2);
        System.out.println(s1);
        System.out.println(s2);
    }
}

个人github代码演示

你可能感兴趣的:(java基础)