单例设计模式(超详细的)

单例设计模式分为饿汉式与懒汉式

单例设计模式:即某个类在整个系统中只能有一个实例对象可以被获取和使用,例如JVM运行的Runtime类

要点分析

1、某各类只能有一个实例
构造器私有化
2、它必须只能由它自己创建这个实例
含有该类的静态变量来保存该类的唯一实例
3、必须提供该实例的获取方法
1、直接暴露(直接公开,静态变量设为public)
2、使用静态变量的get方法获取

饿汉式:在类初始化时就直接创建对象,不存在线程安全问题,但该方式会造成一定的内存浪费

1、直接实例化饿汉式(简洁直观)

/**
 * @Author: yuanzhi...
 * @Date: created in  2020/1/13 14:12
 *
 * 饿汉式:
 *      直接创建这个对象,不管你需不需要(假如该类中有静态方法,
 * 		那么调用时就不需要此类的实例化对象了)
 */
public class SingleTon {

    private static final SingleTon SINGLETON = new SingleTon();
    private SingleTon(){

    }
}

2、使用枚举(最简洁)

/**
 * @Author: yuanzhi...
 * @Date: created in  2020/1/13 17:44
 *
 * 枚举类型:表示该类型的对象是有限的几个
 *  我们可以限定为一个,就成了单例模式
 *  枚举类型的构造器都是私有化的,所以不需要再去处理
 */
public enum SingleTon2 {
    SINGLETON
}
class test{
    public static void main(String[] args) {
        SingleTon2 singleTon2 = SingleTon2.SINGLETON;
        System.out.println(singleTon2);//此处打印出来的并不是类名加hashcode码,而是常量
    }
}

3、使用静态代码块的方式(适合复杂实例化)
single.yml文件(放在src下),或者用single.properties文件(两者区别此处不细说)

#key: value
info: yuanZhi

实现代码:

import java.io.IOException;
import java.util.Properties;

/**
 * @Author: yuanzhi...
 * @Date: created in  2020/1/13 17:52
 */
public class SingleTon3 {
    public static final SingleTon3 SINGLETON;
    private String info;
    static {
        try {
            Properties properties = new Properties();
            properties.load(SingleTon3.class.getClassLoader().getResourceAsStream("single.yml"));
            SINGLETON = new SingleTon3(properties.getProperty("info"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    private 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 + '\'' +
                '}';
    }
}

class test3{
    public static void main(String[] args) {
        SingleTon3 singleTon3 = SingleTon3.SINGLETON;
        System.out.println(singleTon3);
    }
}

懒汉式:延缓式创建对象,即在什么时候需要什么时候创建,所以并不会造成内存的浪费

1、线程不安全(适用于单线程)

import java.util.concurrent.*;

/**
 * @Author: yuanzhi...
 * @Date: created in  2020/1/13 18:20
 *
 *  提供一个获取实例对象的静态方法
 */
public class SingleTon4 {
    private static SingleTon4 singleTon4;
    private SingleTon4() {

    }
    public static SingleTon4 singleTon4(){
        if (singleTon4 == null){
            //就算没有一下线程休眠的代码。也有可能CPU时间到了
            //但是是有概率的,所以可能会是同一个,也可能不是
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            singleTon4 = new SingleTon4();
        }
        return singleTon4;
    }
}

class test4{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /*SingleTon4 singleTon4 = SingleTon4.singleTon4();
        SingleTon4 singleTon41 = SingleTon4.singleTon4();
        System.out.println(singleTon4 == singleTon41);//true
        System.out.println(singleTon4);//SingleTon4@4554617c
        System.out.println(singleTon41);//SingleTon4@4554617c*/

        //线程安全问题(多线程,下面以两个线程为例)
        System.out.println("===========================线程安全问题分割线============================");
        Callable<SingleTon4> callable = new Callable<SingleTon4>() {
            @Override
            public SingleTon4 call() throws Exception {
                return SingleTon4.singleTon4();
            }
        };
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<SingleTon4>future1 = es.submit(callable);
        Future<SingleTon4>future2 = es.submit(callable);
        SingleTon4 singleTon42 = future1.get();
        SingleTon4 singleTon43 = future2.get();
        System.out.println(singleTon42 == singleTon43);//false
        System.out.println(singleTon42);//SingleTon4@6d6f6e28
        System.out.println(singleTon43);//SingleTon4@135fbaa4
        es.shutdown();
    }
}

2、线程安全(适用于多线程)(在上面的基础上以同步的方法加以改善)

import java.util.concurrent.*;

/**
 * @Author: yuanzhi...
 * @Date: created in  2020/1/13 18:20
 *
 *  提供一个获取实例对象的静态方法
 */
public class SingleTon5 {
    private static SingleTon5 singleTon5;
    private SingleTon5() {

    }
    public static SingleTon5 SingleTon5(){
        if (singleTon5 == null) {  //该句为了提高性能,即如果有了就不必在需要锁那些了
            synchronized (SingleTon5.class) {
                if (singleTon5 == null) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    singleTon5 = new SingleTon5();
                }
            }
        }
        return singleTon5;
    }
}

class test5{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<SingleTon5> callable = new Callable<SingleTon5>() {
            @Override
            public SingleTon5 call() throws Exception {
                return SingleTon5.SingleTon5();
            }
        };
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<SingleTon5>future1 = es.submit(callable);
        Future<SingleTon5>future2 = es.submit(callable);
        SingleTon5 SingleTon52 = future1.get();
        SingleTon5 SingleTon53 = future2.get();
        System.out.println(SingleTon52 == SingleTon53);//true
        System.out.println(SingleTon52);//SingleTon5@6d6f6e28
        System.out.println(SingleTon53);//SingleTon5@6d6f6e28
        es.shutdown();
    }
}

3、静态内部类实现(适用于多线程)

/**
 * @Author: yuanzhi...
 * @Date: created in  2020/1/13 19:27
 *
 *  在内部类被加载和初始化的时候才创建对象SINGLE_TON_6
 *      静态内部类不会总动随着外部类的加载和初始化而初始化,它是要单独加载和初始化的
 *      因为是在内部类加载和初始化时创建的,因此是线程安全的
 */
public class SingleTon6 {
    private SingleTon6(){

    }
    private static class Inner{
        private static final SingleTon6 SINGLE_TON_6 = new SingleTon6();
    }

    public static SingleTon6 getSingleTon6(){
        return Inner.SINGLE_TON_6;
    }
}

class test6{
    public static void main(String[] args) {
        SingleTon6 singleTon61 = SingleTon6.getSingleTon6();
        SingleTon6 singleTon62 = SingleTon6.getSingleTon6();
        System.out.println(singleTon61 == singleTon62);//true
        System.out.println(singleTon61);//SingleTon6@4554617c
        System.out.println(singleTon62);//SingleTon6@4554617c
    }
}

小结:

1、如果是饿汉式,枚举形式最简单;
2、如果是懒汉式,静态内部类形式最简单;
作者君总结码字不易,留个赞在走吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值