【设计模式】单例设计模式

8种单例模式

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

  2. 单例模式有八种方式:

    1) 饿汉式(静态常量)
    2) 饿汉式(静态代码块
    3) 懒汉式(线程不安全)
    4) 懒汉式(线程安全,同步方法)
    5) 懒汉式(线程安全,同步代码块)
    6) 双重检查
    7) 静态内部类
    8) 枚举
    之前学java基础的时候页写过单例的笔记,只包括饿汉式和懒汉式

  3. 步骤都一样的(除枚举类):

  1. 构造器私有化 (防止 new )
  2. 类的内部创建对象
  3. 向外暴露一个静态的公共方法。 getInstance

用来测试的代码都一样(除枚举类)

public class SingletonTest1 {

    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleton();
        Singleton singleton2 = Singleton.getSingleton();
        System.out.println(singleton1==singleton2);

    }
}

饿汉式(可用,但可能造成内存浪费)

  1. 饿汉式就是类加载对象就加载,这样如果我们没有用到这个类的时候 就造成了资源浪费.
    但是饿汉式是线程安全的,所以这种单例模式是可用的.

  2. 饿汉式优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同
    步问题。

  3. 饿汉式缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费

这里的类装载就完成实例化,如果是通过getInstance方式的时候导致的类装载,那这不是资源浪费.但是如果不是通过这种方法,而是别的导致类装载的,那就是没有达到懒加载的效果

代码:

class Singleton{


    // 构造器私有化
    private Singleton(){

    }
    //类的内部创建对象
    private static Singleton singleton=  new Singleton();

    //向类的外部暴露一个静态的公共方法
    public static Singleton getSingleton() {
        return singleton;
    }
}

静态代码块

静态代码块的方式就是把 new Singleton()的部分放在了静态代码块里,其余部分不做改动
静态代码块也是在类加载的时候加载的

class Singleton{


    // 构造器私有化
    private Singleton(){

    }
    //类的内部创建对象
    private static Singleton singleton;

    static{
        singleton = new Singleton();
    }

    //向类的外部暴露一个静态的公共方法
    public static Singleton getSingleton() {
        return singleton;
    }
}

懒汉式

懒汉式是不用就不造,到用的时候才造

线程不安全 的 懒汉式 (不能使用)

这样如果多线程的情况下,线程A进入 if(singletonnull)里,但还没创建出来对象,同时线程B也进入 if(singletonnull)里,这样就会创建出来两个对象,就不是单例设计模式了.

class Singleton{


    // 构造器私有化
    private Singleton(){

    }
    //类的内部创建对象
    private static Singleton singleton;

    //向类的外部暴露一个静态的公共方法
    public static Singleton getSingleton() {
        //当调用getInstance 才创建对象
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

线程安全的懒汉式 同步方法(不推荐使用,效率低)

把getSingleton() 加synchronized关键字变成同步方法,这样就既是懒加载又是线程同步了.
但是我们实例只创建一次,大部分情况都只是return就好,这样每次获取实例的时候都要等,效率太低.

class Singleton{
    
    // 构造器私有化
    private Singleton(){

    }
    //类的内部创建对象
    private static Singleton singleton;

    //向类的外部暴露一个静态的公共方法
    public static synchronized Singleton getSingleton() {
        //当调用getInstance 才创建对象
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

懒汉式 同步代码块(不能使用,未实现线程安全)

把if判断的部分用同步代码块包住,想着这样就解决了同步方法效率不高的问题.
但这样并不能实现线程安全.这样还是会假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,
另一个线程也通过了这个判断语句,这时便会产生多个实例

class Singleton{

    // 构造器私有化
    private Singleton(){

    }
    //类的内部创建对象
    private static Singleton singleton;

    //向类的外部暴露一个静态的公共方法
    public static Singleton getSingleton() {
        //同步代码块
        if(singleton==null){
            synchronized(Singleton.class){
                singleton = new Singleton();
            }
        }

        return singleton;
    }
}

双重检查(推荐使用)

  1. 我们可以在同步代码块里再加一个判断 if(singleton==null),这样就解决问题,了
  2. 注意要加volatile
    volatile 可以让我们的修改值立即更新到主存,可以理解成轻量级的synchronized. 这样如果有共享的变量,一旦有修改值立马就刷到主存里.
  3. 优点:线程安全;延迟加载;效率较高
class Singleton{

    // 构造器私有化
    private Singleton(){

    }
    //类的内部创建对象
    private static volatile Singleton singleton;

    //向类的外部暴露一个静态的公共方法
    public static Singleton getSingleton() {
        //同步代码块
        if(singleton==null){
            synchronized(Singleton.class){
                if(singleton==null){
                    singleton = new Singleton();
                }

            }
        }

        return singleton;
    }
}

静态内部类(推荐使用)

静态内部类的特点:

  1. 类Singleton被装载的时候,静态内部类SingletonInstance是不会被装载
  2. getInstance方法调静态内部类的静态属性的时候.会导致静态内部类的装载,且只会装载一次,且装载的时候是线程安全的
  3. 优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
class Singleton{

    // 构造器私有化
    private Singleton(){

    }
    //静态内部类
    private static class SingletonInstance{
        private static final Singleton SINGLETON = new Singleton();
    }
    //向类的外部暴露一个静态的公共方法
    public static Singleton getSingleton() {
        return SingletonInstance.SINGLETON;
    }
}

枚举(推荐使用)

  1. 优点:1) 这借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
  1. 这种方式是Effective Java作者Josh Bloch 提倡的方式
enum Singleton{
    SINGLETON;
    public void method(){
    }
}

测试:

public class SingletonTest1 {

    public static void main(String[] args) {
        Singleton singleton1 = Singleton.SINGLETON;
        Singleton singleton2 = Singleton.SINGLETON;
        System.out.println(singleton1==singleton2);

    }
}


单例模式JDK源码分析

我们JDK中, java.lang.Runtime就是经典的单例模式(饿汉式)

在这里插入图片描述

单例模式注意事项和细节说明

  1. 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
  2. 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new

单例模式的使用场景

单例模式使用的场景:

  • 需要频繁的进行创建和销毁的对象
  • 创建对象时耗时过多或耗费资源过多(即:重量级对象), 但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、 session工厂等)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值