设计模式之单例模式

2020-07-11  本文已影响0人  程序员小杰

转载;https://blog.csdn.net/wangkai525and526/article/details/106995043

什么是单例模式?

单例模式(Singleton Pattern):是指确保一个类在任何情况都绝对只有一个实例,并且提供一个全局访问点。在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例。

单例模式的特点:

单例模式的写法:

一、饿汉式

1、静态常量

public class SingletonTest {

    private static final SingletonTest singleton = new SingletonTest();

    private SingletonTest(){
        System.out.println("无参构造方法执行");
    }

    public static SingletonTest getSingleton(){
       return singleton;
    }

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

2、静态代码块

public class SingletonTest {

    private static final SingletonTest singleton;


    //静态代码块
    static {
        singleton = new SingletonTest();
    }
    private SingletonTest(){
        System.out.println("无参构造方法执行");
    }

    public static SingletonTest getSingleton(){
       return singleton;
    }

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

二、懒汉式
有线程调用getInstance方法的时候,开始进行初始化
优点:
1.开始不占用内存,只有调用了才初始化实例
缺点:
1.并发的时候,可能会出现多个实例。线程不安全,因为并发的时候,会出现创建多个实例的情况

public class SingletonTest2 {
    private static SingletonTest2 singleton = null;

    private SingletonTest2(){
        System.out.println("无参构造方法执行");
    }
    public static SingletonTest2 getSingleton(){
        if(singleton == null){
          return  singleton = new SingletonTest2();
        }
        return singleton;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {

            new Thread(()->{
                System.out.println(SingletonTest2.getSingleton());
            }).start();
        }
    }
}

三、单例模式之线程安全-synchronized(可用)

有线程调用getInstance方法的时候,开始进行初始化
优点:
1.开始不占用内存,只有调用了才初始化
2.线程安全,因为getInstance加上了锁,假如并发访问的话,拿到锁的线程,才能进入方法进行初始化,其他线程排队等待锁
缺点:
1.性能比较低,并发的时候,会出现其他线程等待的情况
2.每一次并发的时候,都会有锁的竞争情况

public class SingletonTest2 {
    private static SingletonTest2 singleton = null;

    private SingletonTest2(){
        System.out.println("无参构造方法执行");
    }
    public  static synchronized SingletonTest2 getSingleton(){
        if(singleton == null){
          return  singleton = new SingletonTest2();
        }
        return singleton;
    }

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

四、单例模式之DCL双重检测+volatile(推荐)

有线程调用getInstance方法的时候,开始进行初始化
优点:
1.开始不占用内存,只有调用了getInstance才初始化
2.第一次并发访问存在锁的竞争情况,后面任何一个一次并发都不会存在锁的竞争

缺点:
1.第一次并发的时候,有锁的竞争情况,存在线程等待

public class SingletonTest2 {
//加上 volatile 关键字,解决 instance = new SingletonTest2() 处的指令重排问题
    private volatile static SingletonTest2 singleton = null;

    private SingletonTest2(){
        System.out.println("无参构造方法执行");
    }
    public  static SingletonTest2 getSingleton(){
        if(singleton == null){
            synchronized (SingletonTest2.class){
                if(singleton == null){
                    return  singleton = new SingletonTest2();
                }
            }
        }
        return singleton;
    }

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

五、静态内部类(推荐)

JVM加载LazyInnerClassSingleton的时候,不会去加载内部类LazyHolder,只有调用getInstance方法的时候,JVM才会去加载LazyHolder,巧妙的避开了线程安全的问题
优点:
1.开始不会占用内存
2.兼顾了synchronized的性能问题
3.利用Java本身的语法特点,巧妙的避免了线程安全的问题

public class SingletonTest3 {

    private SingletonTest3(){
        System.out.println("无参构造方法执行");
    }
    private static class SingletonInstance{
        private static final SingletonTest3 INIT_SINGLETON = new SingletonTest3();
    }
    public static SingletonTest3 getInstance(){
        return SingletonInstance.INIT_SINGLETON;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 60; i++) {
            new Thread(()->{
                System.out.println(SingletonTest3.getInstance());
            }).start();
        }
    }
}
上一篇 下一篇

猜你喜欢

热点阅读