JAVA设计模式之单例模式

单例(Singleton)模式:单例模式是一种创建型模式(将对象的创建与使用分离),是指某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例。

  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。
饿汉式:
/**
 * 单例模式
 * 饿汉式
 * 是否 Lazy 初始化:否
 * 是否多线程安全:是
 * 实现难度:易
 * 描述:这种方式比较常用,但容易产生垃圾对象。
 * 优点:没有加锁,执行效率会提高。
 * 缺点:类加载时就初始化,浪费内存。
 *
 * @author Cocowwy
 * @create 2020-09-09-10:57
 */
public class SingletonDemo1 {
    private static SingletonDemo1 instance = new SingletonDemo1();

    private SingletonDemo1() {
    }

    public static SingletonDemo1 getInstance() {
        return instance;
    }
}

class SingletonDemo1Main {
    public static void main(String[] args) {
        //只能通过静态方法拿到 而不能通过私有化的构造器拿
        SingletonDemo1 instance = SingletonDemo1.getInstance();
        System.out.println(instance);
    }
}

通过代码我们可以看出,在类加载的时候就new SingletonDemo1();这样就避免了懒汉式的getInstance()加锁的缺点,保证了线程的安全,但是在类加载的时候就进行了初始化,会浪费内存。
私有化构造器,暴露一个公有的方法让外界访问同一个对象,该对象是在类加载的时候就创建好了(可以理解饥饿,迫不及待想要创建),所以叫做饿汉式。

懒汉式 不安全:
/**
 * 单例模式
 * 懒汉式
 * 是否 Lazy 初始化:是
 * 是否多线程安全:否
 * 实现难度:易
 *
 * @author Cocowwy
 * @create 2020-09-09-11:09
 */
public class SingletonDemo2 {
    private static SingletonDemo2 instance;

    public SingletonDemo2() {
    }

    public static SingletonDemo2 getInstance() {
        if (null == instance) {
            instance = new SingletonDemo2();
        }
        return instance;
    }
}

class SingletonDemoMain2{
    public static void main(String[] args) {
        System.out.println(SingletonDemo2.getInstance());
    }
}

通过代码我们可以看到,在类加载的时候并未给instance赋值,并且SingletonDemo2 的实例对象只能通过SingletonDemo2 .getInstance()得到,外部是不能通过new SingletonDemo2 ()来得到实例对象的,因为构造器被private 私有化了,只能在内部访问,但是这种方式会存在并发的问题。
私有化构造器,暴露一个公有的方法让外界访问同一个对象,该对象是在需要的时候才创建(可以理解为偷懒,不需要不创建),所以叫做懒汉式。

懒汉式 安全:
/**
 * 单例模式
 * 懒汉式 
 * 是否 Lazy 初始化:是
 * 是否多线程安全:是
 * 实现难度:易
 *
 * @author Cocowwy
 * @create 3030-09-09-11:09
 */
public class SingletonDemo3 {
    private static SingletonDemo3 instance;

    public SingletonDemo3() {
    }

    public static synchronized SingletonDemo3 getInstance() {
        if (null == instance) {
            instance = new SingletonDemo3();
        }
        return instance;
    }
}

class SingletonDemoMain3 {
    public static void main(String[] args) {
        System.out.println(SingletonDemo3.getInstance());
    }
}

在懒汉式的基础上对getInstance加上了锁,解决了并发的问题,这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。

静态内部类:
/**
 * 单例模式
 * 静态内部类
 * 是否 Lazy 初始化:是
 * 是否多线程安全:是
 * 使⽤类的静态内部类实现的单例模式,既保证了线程安全有保证了懒加载,同时不会因为加锁的⽅
 * 式耗费性能。
 *
 * @author Cocowwy
 * @create 2020-09-09-11:27
 */
public class SingletonDemo4 {
    private static class SingletonDemo4Holder {
        private static SingletonDemo4 instance = new SingletonDemo4();
    }

    private SingletonDemo4() {
    }

    public static SingletonDemo4 getInstance() {
        return SingletonDemo4Holder.instance;
    }
}

class SingletonDemo4Main{
    public static void main(String[] args) {
        System.out.println(SingletonDemo4.getInstance());
    }
}

这种方式是SingletonDemo4一旦被加载,但是instance并不会被初始化,因为SingletonDemo4Holder类没有被主动使用,只能显式的调用getInstance方法时,才会显示的装载SingletonDemo4Holder 类,从而实例化instance。

双重锁校验:
/**
 * 单例模式
 * 双重锁校验(线程安全)
 * 是否 Lazy 初始化:是
 * 是否多线程安全:是
 *
 * @author Cocowwy
 * @create 2020-09-09-11:38
 */
public class SingletonDemo5 {
    private volatile static SingletonDemo5 instance;

    public SingletonDemo5() {
    }

    public static SingletonDemo5 getInstance() {
        if (null != instance) {
            return instance;
        }
        synchronized (SingletonDemo5.class) {
            if (null == instance) {
                instance = new SingletonDemo5();
            }
        }
        return instance;
    }
}

class SingletonDemo5Main {
    public static void main(String[] args) {
        System.out.println(SingletonDemo5.getInstance());
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + SingletonDemo5.getInstance());
            }, "Thread" + i).start();
        }
    }
}


volatile关键字,保证可见性,可以防止jvm指令重排优化。
为什么要先进行一次判空?
第一次校验:也就是第一个if(null != instance),这个是为了代码提高代码执行效率,由于单例模式只要一次创建实例即可,所以当创建了一个实例之后,再次调用getInstance方法就不必要进入同步代码块,不用竞争锁。直接返回前面创建的实例即可。
第二次校验:因为volatile 不能保证原子性,即可能出现当Thread1线程修改后并未写会主内存后,Thread进入到了同步代码块的情况。

本人上述代码git地址:Java设计模式

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值