一、什么是单例设计模式
单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。
许多时候整个系统只需要拥有一个全局的对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
二、单例实现的两个步骤
1.将该方法的构造方法定义为私有方法,这样其他的代码就无法通过该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例
2.在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并返回。
三、单例模式的优缺点
优点:
- 在内存中只有一个对象,节省内存空间
- 避免频繁的创建销毁对象,可以提高性能
- 避免对共享资源的多重占用,简化访问
- 为整个系统提供一个全局访问点
缺点:
- 不适用于变化频繁的对象
- 如果实例化的对象长时间不被引用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失。
四、单例的实现
1.饿汉式
public class Singleton1{
private static Singleton1 singleton1 = new Singleton1();
private Singleton1(){}
private static Singleton1 getSingleton1(){
return singleton1;
}
}
类加载是加载一次,只要这个类的字节码文件被类加载器加载到内存当中,就会开始执行初始化,这个时候就会加载静态资源,比如静态代码块和静态初始化(这里是静态初始化)。加载就会生成一个对象,由于只加载一次,就只会生成一个对象。
优点:写法简单,避免了线程同步问题。
缺点:在类的加载的时候就完成了实例化,没有达到懒加载的效果,如果一直没用过这个实例,则会造成内存的浪费。
2.懒汉式
public class Singleton2{
private static Singleton2 singleton2;
private Singleton2(){};
public static Singleton2 getSingleton2(){
if(singleton2 == null){
singleton2 = new Singleton2();
}
return singleton2;
}
}
从懒汉式单例可以看出,单例实例被延迟加载,只有在真正用到的时候才会实例化一个对象并交给自己的引用。
但缺点也很明显,只能在单线程下引用。如果在多线程下,两个线程进入了判断的语句块(此时两个线程共享的singleton2实例都是null),一个线程生成了一个对象,另一个也生成了对象(试问:这两个对象是一样的吗?满足单例设计模式了吗?)这时有人会想了,加个锁,好,那就来加锁
public class Singleton3{
private static Singleton3 singleton3;
private Singleton3(){};
public synchronized static Singleton3 getSingleton3(){
if(singleton3 == null){
singleton3 = new Singleton3();
}
return singleton3;
}
}
线程安全了,一次只能生成一个对象,但线程锁降低了效率(多个线程都在等),这时候还有几种类似的方式,但都不是最好的,接下来介绍最好的方式,也是需要记的,双重加锁机制
public class Singleton4{
private static Singleton4 singleton4;
private Singleton4(){};
public static Singleton4 getSingleton4(){
if(singleton4 == null){
synchronized (Singleton4.class){
if(singleton4 == null)
singleton4 = new Singleton4();
}
}
return singleton4;
}
}
有什么区别呢?各位发现了吗?一方面生成对象的时候只能有一个线程进入,并且判断语句决定了只会生成一个对象,同时,之后再有多个线程进入的时候,不需要加锁了,因为第一个判断语句都过不去,压根不进去执行。
以上代码内容纯手打,如遇运行报错,自行调式。