(一)单例模式(Singleton pattern)

保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

常见应用场景:

  1. Windows的Task Manager(任务管理器)就是很典型的单例模式
  2. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
  3. 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,每次new一个对象去读取。
  4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。
  5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
  6. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
  7. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。 Application 也是单例的典型应用(Servlet编程中会涉及到)
  8. 在Spring中,每个Bean默认就是单例的,这样做的优点是Spring容器可以管理
  9. 在servlet编程中,每个Servlet也是单例
  10. 在spring MVC框架/struts1框架中,控制器对象也是单例

应用场景总结

需要生成唯一序列的环境

需要频繁实例化然后销毁的对象。

创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 

方便资源相互通信的环境

常见的五种单例模式实现方式:

主要:

饿汉式(线程安全,调用效率高。 但是,不能延时加载。)

懒汉式(线程安全,调用效率不高。 但是,可以延时加载。)

其他:

双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题。不建议使用)

静态内部类式(线程安全,调用效率高。 但是,可以延时加载)

枚举单例(线程安全,调用效率高,不能延时加载,实现简单 枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!)

1、懒汉式

/*第一种(懒汉,线程安全):所谓懒汉,就是说当这个对象在需要进行获取的时候,再去对该对象进行创建*/
public class Singleton{
    private static Singleton instance;
    //通过将构造方法私有化达到,不能直接通过new 类名(),创建类的目的!!!
    private Singleton(){}
    public static Singleton getInstance(){
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

/*第二种(懒汉,线程安全):这里的线程安全看起来十分的美好,但是效率却十分的低,所以一般的情况下不会使用这种方式*/
public class Singleton {
    private static Singleton instance;
    private Singleton(){}
    //通过在此使用同步方法,进行将其变成线程安全的!!!
    public static synchronized Singleton getInstance(){
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

2、饿汉式

/*(饿汉 : 线程安全) : 当该类一旦进行加载(classLoader机制),就需要对其该类中的对象进行创建,同样因此也就使其避免了线程的安全问题*/
public class Singleton {
    // 指向自己实例的私有静态引用,主动创建
    private static Singleton instance = new Singleton();

    // 私有的构造方法
    private Singleton(){}

    // 以自己实例为返回值的静态的公有方法,静态工厂方法
    public static Singleton getInstance() {
        return instance;
    }
}

3、双重加锁机制

/*(双重加锁机制:线程安全):其不但保证了单例,而且切实提高了程序运行效率 优点:线程安全;延迟加载;效率较高。*/
public class Singleton{
    private static Singleton instance;
    //程序运行时创建一个静态只读的进程辅助对象
    private static readonly object syncRoot = new object();
    private Singleton() { }
    public static Singleton GetInstance(){
        //先判断是否存在,不存在再加锁处理
        if (instance == null){
            //在同一个时刻加了锁的那部分程序只有一个线程可以进入
            lock (syncRoot){
                if (instance == null){
                    instance = new Singleton();
                }
            }
       }
       return instance;
    }
}

4、静态内部类式 

public class SingletonDemo04 {
	
	private static class SingletonClassInstance { 
		private static final SingletonDemo04 instance = new SingletonDemo04(); 
	} 

	  public static SingletonDemo04 getInstance() { 
		  return SingletonClassInstance.instance; 
	  } 

	  private SingletonDemo04() { 
	  } 
}

5、枚举 

/*(枚举):该方法是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,保证只有一个实例,即使使用反射机制也无法多次实例化一个枚举量*/
public enum SingletonDemo05 {
     /**
     * 定义一个枚举的元素,它就代表了Singleton的一个实例。
     */
    INSTANCE;
    /**
     * 单例可以有自己的操作
     */
    public void singletonOperation(){
        //功能处理
    }
}

优点:

  1. 某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。(这个时候如果不需要多个对象,就可以使用单例设计模式进行搞定)。
  2. 省去了new操作符,降低了系统内存的使用频率,减轻GC压力。这条与第一条的作用就是减少系统开销,降低内存使用。
  3. 有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。有效的对系统进行控制。
  4. 再比如有些软件的配置文件,不管使用该软件当中什么工具,都会对同一个配置文件进行读取和修改,这就是单例设计的思想。使用该思想可以实现某些业务需求。

缺点

  1.  不适用于变化频繁的对象;

  2. 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;

  3. 如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失;

总结

要想实现效率高的线程安全的单例,我们必须注意以下两点:

  1. 尽量减少同步块的作用域;
  2. 尽量使用细粒度的锁。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值