设计模式01:单例Singleton

单例模式

WHAT
单例是全局只有一个实例的代码设计方式

WHY
应用场景:
只需要一个实例,比如各种的工厂类,全局只需要一个就可以了,所以不需要让它被new出多个.
如何实现单例呢?

HOW
实现单例的方式1:饿汉式(最常用的方式,并且是线程安全的)

/**
 * 饿汉式
 *  类加载到内存后,就实例化一个单例,JVM保证线程安全
 *  简单实用,推荐使用!
 *  唯一缺点:不管用到与否,类装载时就完成实例化
 *  Class.forName("")
 *  (话说你不用的,你装载它干啥)
 */
public class HugoFactory {

    // 0,创建需要的实例对象
    private static final HugoFactory hugoFactoryInstance = new HugoFactory();

    // 1,构造方法私有:为了不让别人通过new的方式的创建对象
    private HugoFactory() {

    }

    // 2,创建对象并对外提供获取对象的方法
    public static HugoFactory getInstance() {

        return hugoFactoryInstance;
    }

    // 测试
    public static void main(String[] args) {
        HugoFactory hugoFactory1 = HugoFactory.getInstance();
        HugoFactory hugoFactory2 = HugoFactory.getInstance();
        System.out.println(hugoFactory1 == hugoFactory2);
    }
}

/**
 * 跟01是一个意思
 */
public class Mgr02 {
    private static final Mgr02 INSTANCE;
    static {
        INSTANCE = new Mgr02();
    }

    private Mgr02() {};

    public static Mgr02 getInstance() {
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        Mgr02 m1 = Mgr02.getInstance();
        Mgr02 m2 = Mgr02.getInstance();
        System.out.println(m1 == m2);
    }
}

实现单例的方式2:懒汉式(有线程安全问题)

package com.mashibing.dp.singleton.demo_mine;

/**
 * 懒汉式:获取对象实例的时候加载
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 */
public class HugoFactory02 {

    private static HugoFactory02 INSTANCE;

    // 1,构造方法私有:为了不让别人通过new的方式的创建对象
    private HugoFactory02() {

    }

    // 2,创建对象并对外提供获取对象的方法
    public static HugoFactory02 getInstance() {

        // 如果当前没有实例则创建实例并返回
        if (INSTANCE == null) {
            // 睡眠一会
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new HugoFactory02();
            return INSTANCE;
        }

        return INSTANCE;

    }

    // 测试
    public static void main(String[] args) {
       // 模拟线程安全问题,创建若干线程同时获取INSTANCE实例
        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // 获取实例
                    HugoFactory02 instance = HugoFactory02.getInstance();
                    System.out.println("instance = " + instance);
                }
            }).start();
        }
    }
    /** 测试结果:获取到了不同的实例对象,因为多条线程访问了共享资源
     instance = com.mashibing.dp.singleton.demo_mine.HugoFactory02@260d0705
     instance = com.mashibing.dp.singleton.demo_mine.HugoFactory02@260d0705
     instance = com.mashibing.dp.singleton.demo_mine.HugoFactory02@d438b49
	
	 解决方案:加锁
	 	synchronized同步方法就行了
	 	synchrnized代码块:注意块的范围,要包裹住共享数据
     */
}

实现单例的方式3:懒汉双判空

package com.mashibing.dp.singleton.demo_mine;

/**
 * 懒汉式:获取对象实例的时候加载
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 * 双判空写法
 */
public class HugoFactory03 {

    private static HugoFactory03 INSTANCE;

    // 1,构造方法私有:为了不让别人通过new的方式的创建对象
    private HugoFactory03() {}

    // 2,创建对象并对外提供获取对象的方法,加synchronized使线程同步,解决安全问题
    public static HugoFactory03 getInstance() {

        // 如果当前没有实例则创建实例并返回
        if (INSTANCE == null) {
            synchronized (HugoFactory02.class) {
                if (INSTANCE == null) {
                    // 睡眠一会
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new HugoFactory03();
                    return INSTANCE;
                }
            }
        }
        return INSTANCE;
    }

    // 测试
    public static void main(String[] args) {
        // 模拟线程安全问题,创建若干线程同时获取INSTANCE实例
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // 获取实例
                    HugoFactory03 instance = HugoFactory03.getInstance();
                    System.out.println("instance = " + instance);
                }
            }).start();
        }
    }
    /**
     instance = com.mashibing.dp.singleton.demo_mine.HugoFactory02@260d0705
     instance = com.mashibing.dp.singleton.demo_mine.HugoFactory02@260d0705
     instance = com.mashibing.dp.singleton.demo_mine.HugoFactory02@d438b49
     */
}

实现单例的方式4:静态内部类方式

package com.mashibing.dp.singleton.demo_mine;

/**
 * 静态内部类的方式:内部类负责创建实例对象
 */
public class HugoFactory04 {

    private HugoFactory04() {
    }

    // 静态内部中创建实例,jvm进行管理,保证了线程的安全
    private static class HugoFactoryHolder {
        private static final HugoFactory04 INSTANCE = new HugoFactory04();
    }

    public static HugoFactory04 getInstance(){
        return HugoFactoryHolder.INSTANCE;
    }


    // 测试
    public static void main(String[] args) {
        // 模拟线程安全问题,创建若干线程同时获取INSTANCE实例
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // 获取实例
                    HugoFactory04 instance = HugoFactory04.getInstance();
                    System.out.println("instance = " + instance);
                }
            }).start();
        }
    }

}

实现单例的方式5:枚举方式

package com.mashibing.dp.singleton;

/**
 * 不仅可以解决线程同步,还可以防止反序列化。
 * 枚举类没有构造方法,所以不能被反序列化
 */
public enum HugoFactory05 {

    INSTANCE;

    public void m() {}

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(HugoFactory05.INSTANCE.hashCode());
            }).start();
        }
    }

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值