【多线程】实现单例模式的五种方式

实现单例模式的五种方式

一.什么是单例模式

  • 单例模式指的是在应用整个生命周期内只能存在一个实例。单例模式是一种被广泛使用的设计模式。他有很多好处,能够避免实例对象的重复创建,减少创建实例的系统开销,节省内存。
  • 优点:系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
  • 缺点:当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new,可能会给其他开发人员造成困扰,特别是看不到源码的时候。

二.用饿汉模式实现单列模式

  • 饿汉模式:顾名思义十分着急,这里的饿汉模式是指实例的创建时机早,实例在类加载的时候就创建了,相当于程序一启动,实例就创建好了.
  • 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
  • 缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
    代码实例:
class Singleton{
    private static Singleton singleton = new Singleton();
    public static Singleton getSingleton(){
        return singleton;
    }
    private Singleton(){};
}
public class Test1 {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getSingleton();
        Singleton s2 = Singleton.getSingleton();
        System.out.println(s1==s2);
    }
}

运行结果:
在这里插入图片描述

三.用懒汉模式实现单例模式

  • 懒汉模式:故名思意不着急,此处是指,创建现成的时机是你需要的时候,它才创建.

这个代码需要注意两个点:

  • 1).线程安全问题.如果不加锁就可能创建出两个线程.
    2).指令重排序引起的线程安全问题.
    nstance = new SingletonLazy; 这个代码其实可以拆成三个大步骤(不是三个命令)
    ①.申请一段内存空间 ②.在这个内存上调用构造方法,创建出实例. ③把这个内存地址赋给instance引用变量正常情况下,上述代码是按照123的顺序来执行的.但是编译器也可能优化为132的顺序来执行.对于单线程来讲上述两种执行过程并不会有影响.但对于多线程来说.如果t1线程执行完13.此时instance的指向就不为null了,是一个非空但未实例化的对象,此时如果t2线程调度到执行,到判断是否加锁语句时就不会执行,而是直接返回.如果t2继续使用instance里面的属性和方法就会出现问题(此时属性都是未初始化的全"0"值).就可能引起代码的逻辑出现问题. 解决这个问题需要引入volatile关键字,解决指令重排序的问题
    代码实例:
class SingletonLazy{
    private volatile static  SingletonLazy singletonLazy = null;
    public static Object locker = new Object();
    public static SingletonLazy getSingletonLazy(){
            synchronized (locker) {
            //保证线程安全
                if (singletonLazy == null) {
                    //判断是否要创建对象
                    singletonLazy = new SingletonLazy();
                }
            }
        }
        return singletonLazy;
    }
    private SingletonLazy(){

    }
}
public class Test2 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getSingletonLazy();
        SingletonLazy s2 = SingletonLazy.getSingletonLazy();
        System.out.println(s1==s2);
    }
}

运行结果:
在这里插入图片描述

此时会出现一个优化的点,就是在保证线程安全的前提下,实例被创建出来以后,每次获取实例的时候都需要再次加锁判断,这样会减低代码的执行效率.故此引出下一种创建单例模式的方法—双重校验锁.

四.用双重校验锁实现单例模式

优点:线程安全;延迟加载;效率较高。
代码实例:

package Demo2;
class SingletonLazy{
    private volatile static  SingletonLazy singletonLazy = null;
    public static Object locker = new Object();
    public static SingletonLazy getSingletonLazy(){
        if(singletonLazy==null) {
            //判断是否要加锁
            synchronized (locker) {
                if (singletonLazy == null) {
                    //判断是否要创建对象
                    singletonLazy = new SingletonLazy();
                }
            }
        }
        return singletonLazy;
    }
    private SingletonLazy(){

    }
}
public class Test2 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getSingletonLazy();
        SingletonLazy s2 = SingletonLazy.getSingletonLazy();
        System.out.println(s1==s2);
    }
}

运行结果:
在这里插入图片描述

五.用静态内部类实现单列模式

静态内部类实现单例模式保证线程安全的方法和饿汉模式一样,类的静态属性只会在第一次加载类的时候初始化.所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

代码实例:

//使用静态内部类实现单例模式--线程安全
class SingletonStaticInner {
    private SingletonStaticInner() {

    }
    private static class SingletonInner {
        private static final SingletonStaticInner singletonStaticInner = new SingletonStaticInner();
    }
    public static SingletonStaticInner getInstance() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return SingletonInner.singletonStaticInner;
    }
}
public class Test3 {
    public static void main(String[] args) {
        SingletonStaticInner singletonStaticInner1 = SingletonStaticInner.getInstance();
        SingletonStaticInner singletonStaticInner2 = SingletonStaticInner.getInstance();
        System.out.println(singletonStaticInner1==singletonStaticInner2);
    }
}

运行结果:
在这里插入图片描述

六.用枚举类实现单例模式

借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。

public enum Singleton {
    INSTANCE;
    private Singleton() {}
}
  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值