0
点赞
收藏
分享

微信扫一扫

Java---23种设计模式(五)------单例模式(单态)模式


1.什么是单例模式?

  单例模式的概念:

它的基本概念是一个类只生成一个实例。

 

在一个系统中,JVM中,只能存在有一个对象的实例,所有程序的处理都只能调用这一个对象完成相关逻辑操作,单态模式有点在于减少了new关键字的调用,实例的初始化,节省了系统内存开销。单态模式的难点在于多线程的处理,写法很多,真正能够执行的,考虑的,难点在于线程的安全问题!!!

 

 单例模式有以下特点:
  1、单例类只能有一个实例。
  2、单例类必须自己创建自己的唯一实例
  3、单例类必须给所有其他对象提供这一实例。

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

 

2.单例模式的使用案例

使用场景:

  • 1、要求生产唯一序列号。
  • 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  • 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。

   1.在计算机系统中,线程池、缓存、日志对象、对话框打印机显卡的驱动程序对象常被设计成单例。

     这些应用都或多或少具有资源管理器的功能

     每台计算机可以有若干个打印机,但只能有一个Printer Spooler,

      以避免两个打印作业同时输出到打印机中。

      每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头
---------------------

生活中的例子: 

 

  • 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
  • 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。

3.单例模式的类图

Java---23种设计模式(五)------单例模式(单态)模式_初始化

 

 

4.单例模式的用法

 

 

单例模式的实现有多种方式,如下所示:

1、懒汉式,线程不安全

是否 Lazy 初始化:

是否多线程安全:

实现难度:

描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

package Singleton;

public class LazySingleton {

/**懒汉式设计模式*/
private static LazySingleton lazySingleton;
private LazySingleton()
{}
public static LazySingleton getLazySingleton()
{
if(lazySingleton==null)
{

lazySingleton=new LazySingleton();
System.out.println("懒汉式线程不安全对象创建成功");


}

return lazySingleton;


}

2、懒汉式,线程安全

是否 Lazy 初始化:

是否多线程安全:

实现难度:

描述:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。

 

package Singleton;

public class LazySingletonSafe {
private static LazySingletonSafe lazySingletonSafe;

private LazySingletonSafe() {
}

public static synchronized LazySingletonSafe getLazySingletonSafe() {
if(lazySingletonSafe==null)
{
lazySingletonSafe=new LazySingletonSafe();
System.out.println("懒汉式线程安全对象创建成功");

}
return lazySingletonSafe;
}
}

 

3、饿汉式

是否 Lazy 初始化:

是否多线程安全:

实现难度:

描述:这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。

 

package Singleton;

public class HardSingletonSafe {
private static HardSingletonSafe HardSingletonSafe;
private HardSingletonSafe()
{}
public static HardSingletonSafe getHardSingletonSafen()
{

System.out.println("饿汉式线程安全对象创建成功");




return HardSingletonSafe;


}

}

 

4、双检锁/双重校验锁(DCL,即 double-checked locking)

JDK 版本:JDK1.5 起

是否 Lazy 初始化:

是否多线程安全:

实现难度:较复杂

描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
getInstance() 的性能对应用程序很关键。

 

package Singleton;

public class doublecheckedlockingSingleton {

//双检锁/双重校验锁
private volatile static doublecheckedlockingSingleton doublecheckedlockingSingleton;

private doublecheckedlockingSingleton() {
}

public static doublecheckedlockingSingleton getdoublecheckedlockingSingleton() {
synchronized (doublecheckedlockingSingleton.class) {
if (doublecheckedlockingSingleton == null) {

doublecheckedlockingSingleton = new doublecheckedlockingSingleton();
System.out.println("双检锁/双重校验锁线程安全对象创建成功");


}
}

return doublecheckedlockingSingleton;

}
}

实现按效果:

Java---23种设计模式(五)------单例模式(单态)模式_初始化_02

留坑代填:

           5、登记式/静态内部类

           6、枚举

 

5.单例模式的优点和缺点

 

  • 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
  • 2、避免对资源的多重占用(比如写文件操作)。

缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

以及线程安全问题

 

 

 

  

举报

相关推荐

0 条评论