什么是单例模式?
单例模式就是保证系统中一个类只有一个实例。即一个类只有一个对象实例
特点:
1,一个类只能有一个实例
2,自己创建这个实例
3,整个系统都要使用这个实例
他有两种模式,
1.懒汉模式:一开始不产生实例,当调用的时候判断是否有这个类的实例,如果没有采取创建
public class SingletonClass{ private static SingletonClass instance=null; public static synchronized SingletonClass getInstance(){ if(instance==null){ instance=new SingletonClass(); } return instance; } private SingletonClass(){ } }
2.饿汉模式:类加载的时候就生成这个类的实例,以后调用直接返回这个类的实例
public class Singleton{ //在自己内部定义自己的一个实例,只供内部调用 private static final Singleton instance = new Singleton(); private Singleton(){ //do something } //这里提供了一个供外部访问本class的静态方法,可以直接访问 public static Singleton getInstance(){ return instance; } }
使用注意事项:
1.使用时不能用反射模式创建单例,否则会实例化一个新的对象
2.使用懒单例模式时注意线程安全问题
3.饿单例模式和懒单例模式构造方法都是私有的,因而是不能被继承的,有些单例模式可以被继承(如登记式模式)
适用场景:
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如:
1.需要频繁实例化然后销毁的对象。
2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
3.有状态的工具类对象。
4.频繁访问数据库或文件的对象。
5.生成系统主键
以下都是单例模式的经典使用场景:
1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
2.控制资源的情况下,方便资源之间的互相通信。如线程池等。
优化:
当使用懒汉模式时,我们本来只是想让new这个操作并行就可以了,现在,只要是进入 getInstance()的线程都得同步啊,注意,创建对象的动作只有一次,后面的动作全是读取那个成员变量,这些读取的动作不需要线程同步啊。这样的 作法感觉非常极端啊,为了一个初始化的创建动作,居然让我们达上了所有的读操作,严重影响后续的性能啊!
我们想办法在进行线程同步之前在检查一下是否初产生了实例,修改懒汉模式如下
public class Singleton { private static Singleton instance; private Singleton(){} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
1. 第一个条件是说,如果实例创建了,那就不需要同步了,直接返回就好了。
2. 不然,我们就开始同步线程。
3. 第二个条件是说,如果被同步的线程中,有一个线程创建了对象,那么别的线程就不用再创建了。
这种修改后的懒汉模式也叫“双重检查”,这还不是最好的解决方案,可以使用内部静态类来解决。
由于静态单例对象没有作为Singleton的成员变量直接实例化,因此类加载时不会实例化Singleton,第一次调用getInstance()时将加载内部类HolderClass,在该内部类中定义了一个static类型的变量instance,此时会首先初始化这个成员变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。由于getInstance()方法没有任何线程锁定,因此其性能不会造成任何影响。
通过使用IoDH,我们既可以实现延迟加载,又可以保证线程安全,不影响系统性能,不失为一种最好的Java语言单例模式实现方式(其缺点是与编程语言本身的特性相关,很多面向对象语言不支持IoDH)
代码如下
public class Singleton { private Singleton() { } private static class HolderClass { private final static Singleton instance = new Singleton(); } public static Singleton getInstance() { return HolderClass.instance; } }