设计模式系列----单例模式的详细解读

1.单例模式

简单点说,就是一个应用程序中,某个类的实例对象只有一个,你没有办法去new,因为构造器是被private修饰的,一般通过getInstance()的方法来获取它们的实例。

getInstance()的返回值是一个对象的引用,并不是一个新的实例,所以不要错误的理解成多个对象。单例模式实现起来也很容易,直接看demo吧

1.1 饿汉式(静态代码块)

public class Singleton {
private static Singleton singleton;
//构造器私有化,外部不能new
private Singleton() {
}
//静态代码块,只执行一次
static{
instance=new Singleton;
}
//返回静态代码块创建的实例
public static Singleton getInstance() {
 
 return instance;
}
}

评价:能用,但是耗费内存

## 1.2 饿汉式(静态常量)
public class Singleton {  
   private static Singleton instance = new Singleton();  
   private Singleton (){}  
   public static Singleton getInstance() {  
   return instance;  
   }  
}

评价:能用,但是耗费内存,第三推荐度

2.懒汉式(线程安全)

public class Singleton {  
   private static Singleton instance;  
   private Singleton (){}  
   public static synchronized Singleton getInstance() {  
   if (instance == null) {  
       instance = new Singleton();  
   }  
   return instance;  
   }  
}

评价:当使用时才会创建,并且只会创建一次;线程安全,但是效率太低了,最好别用

3.静态内部类


```bash
public class Singleton {  
   private static class SingletonHolder {  
   private static final Singleton INSTANCE = new Singleton();  
   }  
   private Singleton (){}  
   public static final Singleton getInstance() {  
   return SingletonHolder.INSTANCE;  
   }  
}
``

`

评价:这种方式使用了类装载的机制,保证了初始化实例时只有一个线程,在外部类被装载时,静态内部类并不会被装载,实现了懒加载。同时类的静态变量只会在第一次加载时初始化,所以在这里JVM帮我们保证了线程的安全性,在类初始化时,别的线程是无法进入的。
所以,第一推荐度,推荐使用

4.双重检查

public class Singleton {  
   private volatile static Singleton singleton;  
   private Singleton (){}  
   public static Singleton getSingleton() {  
   if (singleton == null) {  
       synchronized (Singleton.class) {  
       if (singleton == null) {  
           singleton = new Singleton();  
       }  
       }  
   }  
   return singleton;  
   }  
}

评价:既实现了线程安全,有实现了懒加载(lazy loading),由于java内存模型一些原因偶尔失败,在实际开发中,也还可以,第四推荐度,推荐使用

5.枚举

public enum Singleton {  
   INSTANCE;  
   public void whateverMethod() {  
   }  
}
下面是测试一下枚举如何调用

public static void main(String args[]){
Singleton instance=Singleton.INSTANCE;
}

评价:这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,很强,也简单至极(但是jdk1.5 才有的枚举,部分人有些生疏),第二推荐度,所以推荐使用

单例模式的使用场景:
①在JDK中,java.lang.RunTime源码就是使用了饿汉式的单例模式
②需要频繁实例化然后销毁的对象。
③创建对象时耗时过多或者耗资源过多,但又经常用到的对象
④有状态的工具类对象
⑤频繁访问数据库或文件的对象

生活中的实际应用场景:

1 .外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件 
2. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~ 
3. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。 
4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。 
5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。 
6. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。 
7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。 
8. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。 
9. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。

单例模式的优点:
1.在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例
2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
3.提供了对唯一实例的受控访问。
4.由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。
5.允许可变数目的实例。
6.避免对共享资源的多重占用。
单例模式的缺点:
1.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
2.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
3.单例类的职责过重,在一定程度上违背了“单一职责原则”。
4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值