前言
本文章根据刘伟(Sunny) 的设计模式一书记录的笔记,感谢作者的知识分享。
定义
单例模式
(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
- 单例模式有三个要点:
- 一 是某个类只能有一个实例;
- 二 是它必须自行创建这个实例;
- 三 是它必须自行向整个系统提供这个实例。
实现
实现一
最简单的实现,存在线程安全的问题
public class TaskManage {
/**
* 私有的构造方法
*/
private TaskManage(){}
private static TaskManage install = null;
/**
* 自己创建实例,并提供给外界使用
*/
public static TaskManage getInstall(){
if(install==null){
install = new TaskManage();
}
return install;
}
public void paint(){
System.out.println("显示任务管理器");
}
}
面临多线程的时候会出现构建多个实例对象的问题
实现二
基于多线程安全改造
饿汉式实现单例,缺点就是可能会浪费资源
public class EagerSingleton {
/**
* 类被加载的时候就进行单例创建
*/
private static EagerSingleton install = new EagerSingleton();
/**
* 私有构造函数
*/
private EagerSingleton(){
}
/**
* 给外面提供单例对象
*/
public static EagerSingleton getInstall(){
return install;
}
public void paint(){
System.out.println("饿汉单例模式");
}
}
懒汉式实现单例(直接是双重检查锁定实现的懒汉式单例类)
public class LazySingleton {
// private static LazySingleton install = null;
//双重判断的时候添加关键字volatile
private volatile static LazySingleton install = null;
private LazySingleton(){
}
/**
* 添加synchronized关键字,保证了多线程的同步
* 但是由于synchronized修饰的是getInstall方法,所以性能比较低,所以可以进行修改
* @return
*/
// synchronized public static LazySingleton GetInstance(){
// if(install==null){
// install = new LazySingleton();
// }
// return install;
// }
/**
* 只有在单例对象为null的时候,才会进行锁的判断
* 但是当两个线程同时进入锁判断的时候,一个线程获得锁进行单例的实例化的结束之后,另外一个线程还是会继续实例化单例对象,所以需要添加二重判断
* @return
*/
// public static LazySingleton getInstall(){
// if(install==null){
// synchronized (LazySingleton.class){
// install = new LazySingleton();
// }
// }
// return install;
// }
/**
* 添加二重判断之后,就算另外一个线程继续实例化,但是之后会进行install判断才决定是否创建
* 这时候需要在install前面加上volatile关键字,保证多线程操作的正确性
* 尽管如此,加锁之后性能还是有影响的
* @return
*/
public static LazySingleton getInstall(){
if(install==null){
synchronized (LazySingleton.class){
if(install==null){
install = new LazySingleton();
}
}
}
return install;
}
}
实现三
综合懒汉饿汉的优缺点的ioDH实现单例,将线程安全交托给JVM负责
public class Singleton {
private Singleton(){}
static class IohdSingleton{
public static Singleton install = new Singleton();
}
public static Singleton getInstall(){
return IohdSingleton.install;
}
}
总结
-
1.主要优点
单例模式的主要优点如下:- (1) 单例模式提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。
- (2) 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
- (3) 允许可变数目的实例。基于单例模式我们可以进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例,既节省系统资源,又解决了单例单例对象共享过多有损性能的问题。
-
2.主要缺点
单例模式的主要缺点如下:- (1) 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
- (2) 单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。
- (3) 现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的共享对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态的丢失。
-
3.适用场景
在以下情况下可以考虑使用单例模式:- (1) 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。
- (2) 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
思考