设计模式:单例模式

设计模式类型

  1. 创建型模式:单例模式、工厂模式、抽象工厂模式、原型模式、建造者模式

  2. 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式

  3. 行为模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、责任链模式(职责链模式)

 

单例模式介绍

 所谓类的单例设计模式,就是采取一定的方法保证在整个软件系统中对某个类只存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session对象。SessionFactory并不是轻量级的,一般情况下,一个项目通常只有一个SessionFactory就够,这就会用到单例模式

 

单例模式的七种方式

  • 饿汉式(静态常量)
  • 饿汉式(静态代码块)
  • 懒汉式(线程不安全)
  • 懒汉式(线程安全,同步方法)
  • 双重检查
  • 静态内部类
  • 枚举

 

单例模式代码

 饿汉式(静态常量)

  1. 构造器私有化
  2. 类的内部创建对象
  3. 向外暴露一个静态的公共方法
package com.zjh.designmodel.singleton;

/**
 * @Author: zjh
 * @Description: 饿汉式(静态变量)
 * @Date: Created in 2020/4/27 20:15
 * Modified by:
 */
public class Singleton1 {
//    1、构造器私有化
    private Singleton1(){}
//    2、类的内部创建对象
    private final static Singleton1 INSTANCE=new Singleton1();
//    3、向外暴露一个静态的公共方法
    public static Singleton1 getInstance(){
        return INSTANCE;
    }
}
package com.zjh.designmodel.singleton;

/**
 * @Author: zjh
 * @Description: 单例模式测试类
 * @Date: Created in 2020/4/27 20:20
 * Modified by:
 */
public class SingletonTest {
    public static void main(String[] args) {
        //看是否是单例模式 判断实例对象是否是同一个
        Singleton1 instance = Singleton1.getInstance();
        Singleton1 instance2 = Singleton1.getInstance();
        System.out.println(instance == instance2);//true
    }
}

饿汉式(静态常量)优点:

这种写法比较简单,就是在类装载的时候就完成实例化,可以避免线程同步问题

饿汉式(静态常量)缺点:

在类装载的时候就完成实例化没有达到懒加载的效果,如果从始至终都没有用该对象那么就会造成内存的浪费

 

饿汉式(静态代码块)

  1. 构造器私有化
  2. 声明
  3. 静态代码块实例化
  4. 向外暴露一个静态的公共方法
package com.zjh.designmodel.singleton;

/**
 * @Author: zjh
 * @Description:饿汉模式(静态代码块)
 * @Date: Created in 2020/4/27 20:38
 * Modified by:
 */
public class Singleton2 {
    //1.构造器私有化
    private Singleton2(){}
    //2.声明
    private static  Singleton2 instance;
    //3.静态代码块实例话对象
    static {
        instance=new Singleton2();
    }
    //4.提供一个公有方法
    public static Singleton2 getInstance(){
        return instance;
    }
}
package com.zjh.designmodel.singleton;

/**
 * @Author: zjh
 * @Description: 单例模式测试类
 * @Date: Created in 2020/4/27 20:20
 * Modified by:
 */
public class SingletonTest {
    public static void main(String[] args) {
        //看是否是单例模式 判断实例对象是否是同一个
//        Singleton1 instance = Singleton1.getInstance();
//        Singleton1 instance2 = Singleton1.getInstance();
//        System.out.println(instance == instance2);//true

        Singleton2 instance3=Singleton2.getInstance();
        Singleton2 instance4=Singleton2.getInstance();
        System.out.println(instance3 == instance4);//true

    }
}

饿汉式(静态代码块)优点:

这种写法比较简单,就是在类装载的时候就完成实例化,可以避免线程同步问题

饿汉式(静态代码块)缺点:

在类装载的时候就完成实例化没有达到懒加载的效果,如果从始至终都没有用该对象那么就会造成内存的浪费

 

懒汉式(线程不安全)

package com.zjh.designmodel.singleton;

/**
 * @Author: zjh
 * @Description: 懒汉式(线程不安全)
 * @Date: Created in 2020/4/28 9:53
 * Modified by:
 */
public class Singleton3 {
    //1.私有化构造器
    private Singleton3(){
        //输出一句话 每次新生产一个对象的时候就会调用这句话
        System.out.println("创建了一个对象");
    };
    //2.声明
    private static Singleton3 instance;
    //3.提供一个公共方法实例化对象
    public static Singleton3 getInstance(){
        if(instance==null){
            instance=new Singleton3();
        }
        return instance;
    }
}
package com.zjh.designmodel.singleton;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Author: zjh
 * @Description: 单例模式测试类
 * @Date: Created in 2020/4/27 20:20
 * Modified by:
 */
public class SingletonTest {
    public static void main(String[] args) {

        //看是否是单例模式 判断实例对象是否是同一个 饿汉式(静态常量)
//        Singleton1 instance = Singleton1.getInstance();
//        Singleton1 instance2 = Singleton1.getInstance();
//        System.out.println(instance == instance2);//true

//        饿汉式(静态代码块)
//        Singleton2 instance3=Singleton2.getInstance();
//        Singleton2 instance4=Singleton2.getInstance();
//        System.out.println(instance3 == instance4);//true

        //单线程情况下 懒汉式(线程不安全)
        Singleton3 instance3 = Singleton3.getInstance();
        Singleton3 instance4 = Singleton3.getInstance();
        System.out.println(instance3 == instance4);//true

        // 懒汉式(线程不安全) 多线程情况下回输出多条构造器里面的语句
        ExecutorService threadpool = Executors.newFixedThreadPool(20);
        for(int i = 0 ; i <20 ; i++) {
            threadpool.execute(()-> {
                Singleton3 singleton3=Singleton3.getInstance();
            });
        }
    }
}

懒汉式(线程不安全)优点:

这种写法使用到了懒加载 的效果,但是只能在单线程情况下使用

饿汉式(线程不安全)线程不安全的原因:

在多线程情况下,当第一个线程进入到if(instance==null)这行代码的时候这个线程正好被挂起,然后另一个线程执行完操作实例化一个对象以后第一个线程继续执行就会在实例化一个对象,所以在多线程下是不安全的,不推荐使用。

 

懒汉式(线程安全,同步方法)

package com.zjh.designmodel.singleton;

/**
 * @Author: zjh
 * @Description:懒汉模式(线程安全,同步方法)
 * @Date: Created in 2020/4/28 10:42
 * Modified by:
 */
public class Singleton4 {
    //1.私有化构造器
    private Singleton4(){
        //输出一句话 每次新生产一个对象的时候就会调用这句话
        System.out.println("创建了一个对象");
    };
    //2.声明
    private static Singleton4 instance;
    //3.提供一个公共方法实例化对象 用synchronized关键字枷锁
    public static synchronized Singleton4 getInstance(){
        if(instance==null){
            instance=new Singleton4();
        }
        return instance;
    }
}

懒汉式(线程安全,同步方法)优点:

这种写法使用到了懒加载 的效果,也解决了多线程下线程不安全的问题

饿汉式(线程安全,同步方法)缺点:

效率太低了,每个线程在想获得类的实例时候即执行getInstance()方法都要进行同步,但在实际中我们其实只需要执行一次实例化就可以后面线程想获得该实例我们直接return就好,所以不推荐使用

 

双重检查

package com.zjh.designmodel.singleton;

/**
 * @Author: zjh
 * @Description: 单例模式 双重检查
 * @Date: Created in 2020/4/28 11:08
 * Modified by:
 */
public class Singleton5 {
    //构造器私有化
    private Singleton5(){};
    //声明
    private static Singleton5 instance;
    //双重检查
    public static Singleton5 getInstance(){
        if(instance ==null){
            synchronized (Singleton5.class){
                if(instance == null){
                    instance=new Singleton5();
                }
            }
        }
        return instance;
    }
}

双重检查优点:

这种写法在多线程下是经常使用的,上述代码我们进行了两次if判断,这样所有线程都会在synchronized外面等着,当第一个线程实例完以后第二个线程进来还会进行一次检查所以实现了线程安全并且也达到了懒加载的效果(避免内存浪费)

 

静态内部类

package com.zjh.designmodel.singleton;

/**
 * @Author: zjh
 * @Description: 单例模式(静态内部类)
 * @Date: Created in 2020/4/28 11:19
 * Modified by:
 */
public class Singleton6 {
    //1.构造器私有化
    private Singleton6() {
    }

    //声明内部类
    private static class SingletonInstance {
        private static final Singleton6 INSTANCE = new Singleton6();
    }

    //提供公共的对外方法
    public static Singleton6 getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

静态内部类优点:

这种写法采用了类装载的机制来保证初始化实例时只有一个线程即多线程下是线程安全的,并且静态内部类SingletonInstance在类装载的时候也不会立即被装载,而是在你调用getInstance方法是才会去装载,所以也达到了懒加载的效果,推荐使用。

 

枚举

package com.zjh.designmodel.singleton;

import org.springframework.util.StringUtils;

/**
 * @Author: zjh
 * @Description: 单例模式(枚举)
 * @Date: Created in 2020/4/28 11:36
 * Modified by:
 */
public enum Singleton7 {
    INSTANCE;
}
package com.zjh.designmodel.singleton;


/**
 * @Author: zjh
 * @Description: 单例模式测试类
 * @Date: Created in 2020/4/27 20:20
 * Modified by:
 */
public class SingletonTest {
    public static void main(String[] args) {

        //看是否是单例模式 判断实例对象是否是同一个 饿汉式(静态常量)
//        Singleton1 instance = Singleton1.getInstance();
//        Singleton1 instance2 = Singleton1.getInstance();
//        System.out.println(instance == instance2);//true

//        饿汉式(静态代码块)
//        Singleton2 instance3=Singleton2.getInstance();
//        Singleton2 instance4=Singleton2.getInstance();
//        System.out.println(instance3 == instance4);//true

//        //单线程情况下 懒汉式(线程不安全)
//        Singleton6 instance3 = Singleton6.getInstance();
//        Singleton6 instance4 = Singleton6.getInstance();
//        System.out.println(instance3 == instance4);//true
//
//        // 懒汉式(线程不安全) 多线程情况下回输出多条构造器里面的语句
//        ExecutorService threadpool = Executors.newFixedThreadPool(20);
//        for(int i = 0 ; i <20 ; i++) {
//            threadpool.execute(()-> {
//                Singleton3 singleton3=Singleton3.getInstance();
//            });
//        }
//

        //单例模式(枚举)
        Singleton7 instance = Singleton7.INSTANCE;
        Singleton7 instance2 = Singleton7.INSTANCE;
        System.out.println(instance == instance2);//true


    }
}

枚举优点:

这种写法可以保证多线程下线程安全的问题并且还能防止反序列化重新创建新的对象,这种方式Effective Java作者Josh Bloch 提倡的的方式

 

JDK源码利用单例模式(饿汉式)

 

单例模式总结:

  1. 当一个类的对象只需要或者只可能有一个时,应该考虑单例模式。
  2. 如果一个类的实例应该在JVM初始化时被创建出来,应该考虑使用饿汉式。
  3. 如果一个类的实例不需要预先被创建,也许这个类的实例并不一定能用得上,也许这个类的实例创建过程比较耗费时间,也许就是真的没必要提前创建。那么应该考虑懒汉式。
  4. 在使用懒汉式单例的时候,应该考虑到线程的安全性问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值